2016-12-19 21:38:06 +00:00
|
|
|
package lfsapi
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
2016-12-20 20:53:26 +00:00
|
|
|
"encoding/json"
|
2016-12-19 21:38:06 +00:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2016-12-20 00:14:03 +00:00
|
|
|
"net/http/httptest"
|
2016-12-19 21:38:06 +00:00
|
|
|
"strings"
|
2016-12-20 00:14:03 +00:00
|
|
|
"sync/atomic"
|
2016-12-19 21:38:06 +00:00
|
|
|
"testing"
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
"github.com/git-lfs/git-lfs/creds"
|
2016-12-19 21:38:06 +00:00
|
|
|
"github.com/git-lfs/git-lfs/errors"
|
2017-10-26 22:29:43 +00:00
|
|
|
"github.com/git-lfs/git-lfs/git"
|
2018-09-06 21:42:41 +00:00
|
|
|
"github.com/git-lfs/git-lfs/lfshttp"
|
2016-12-20 00:14:03 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
2016-12-19 21:38:06 +00:00
|
|
|
)
|
|
|
|
|
2016-12-20 20:53:26 +00:00
|
|
|
type authRequest struct {
|
|
|
|
Test string
|
|
|
|
}
|
|
|
|
|
2016-12-20 16:40:58 +00:00
|
|
|
func TestAuthenticateHeaderAccess(t *testing.T) {
|
2018-09-20 17:21:42 +00:00
|
|
|
tests := map[string]AccessMode{
|
2016-12-20 16:40:58 +00:00
|
|
|
"": BasicAccess,
|
|
|
|
"basic 123": BasicAccess,
|
|
|
|
"basic": BasicAccess,
|
|
|
|
"unknown": BasicAccess,
|
|
|
|
"NTLM": NTLMAccess,
|
|
|
|
"ntlm": NTLMAccess,
|
|
|
|
"NTLM 1 2 3": NTLMAccess,
|
|
|
|
"ntlm 1 2 3": NTLMAccess,
|
|
|
|
"NEGOTIATE": NTLMAccess,
|
|
|
|
"negotiate": NTLMAccess,
|
|
|
|
"NEGOTIATE 1 2 3": NTLMAccess,
|
|
|
|
"negotiate 1 2 3": NTLMAccess,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, key := range authenticateHeaders {
|
|
|
|
for value, expected := range tests {
|
|
|
|
res := &http.Response{Header: make(http.Header)}
|
|
|
|
res.Header.Set(key, value)
|
|
|
|
t.Logf("%s: %s", key, value)
|
|
|
|
assert.Equal(t, expected, getAuthAccess(res))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-20 00:14:03 +00:00
|
|
|
func TestDoWithAuthApprove(t *testing.T) {
|
|
|
|
var called uint32
|
|
|
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
atomic.AddUint32(&called, 1)
|
2016-12-20 22:37:11 +00:00
|
|
|
assert.Equal(t, "POST", req.Method)
|
2016-12-20 00:14:03 +00:00
|
|
|
|
2016-12-20 20:53:26 +00:00
|
|
|
body := &authRequest{}
|
|
|
|
err := json.NewDecoder(req.Body).Decode(body)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, "Approve", body.Test)
|
|
|
|
|
2016-12-20 22:37:11 +00:00
|
|
|
w.Header().Set("Lfs-Authenticate", "Basic")
|
2016-12-20 00:14:03 +00:00
|
|
|
actual := req.Header.Get("Authorization")
|
|
|
|
if len(actual) == 0 {
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := "Basic " + strings.TrimSpace(
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("user:pass")),
|
|
|
|
)
|
|
|
|
assert.Equal(t, expected, actual)
|
|
|
|
}))
|
|
|
|
defer srv.Close()
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
cred := newMockCredentialHelper()
|
2018-10-25 21:30:45 +00:00
|
|
|
c, err := NewClient(lfshttp.NewContext(git.NewReadOnlyConfig("", ""),
|
|
|
|
nil, map[string]string{
|
|
|
|
"lfs.url": srv.URL + "/repo/lfs",
|
|
|
|
},
|
|
|
|
))
|
2017-01-06 15:14:09 +00:00
|
|
|
require.Nil(t, err)
|
2018-09-19 15:05:48 +00:00
|
|
|
c.Credentials = cred
|
2016-12-20 00:14:03 +00:00
|
|
|
|
2018-09-24 18:50:09 +00:00
|
|
|
assert.Equal(t, NoneAccess, c.Endpoints.AccessFor(srv.URL+"/repo/lfs").mode)
|
2016-12-20 00:14:03 +00:00
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
req, err := http.NewRequest("POST", srv.URL+"/repo/lfs/foo", nil)
|
2016-12-20 00:14:03 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
|
2016-12-20 21:03:00 +00:00
|
|
|
err = MarshalToRequest(req, &authRequest{Test: "Approve"})
|
2016-12-20 00:14:03 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
|
2018-10-02 23:47:10 +00:00
|
|
|
res, err := c.DoWithAuth("", c.Endpoints.AccessFor(srv.URL+"/repo/lfs"), req)
|
2016-12-20 00:14:03 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
2018-09-19 15:05:48 +00:00
|
|
|
assert.True(t, cred.IsApproved(creds.Creds(map[string]string{
|
2016-12-20 00:14:03 +00:00
|
|
|
"username": "user",
|
|
|
|
"password": "pass",
|
|
|
|
"protocol": "http",
|
|
|
|
"host": srv.Listener.Addr().String(),
|
|
|
|
})))
|
2018-09-24 18:50:09 +00:00
|
|
|
assert.Equal(t, BasicAccess, c.Endpoints.AccessFor(srv.URL+"/repo/lfs").mode)
|
2016-12-20 00:14:03 +00:00
|
|
|
assert.EqualValues(t, 2, called)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDoWithAuthReject(t *testing.T) {
|
|
|
|
var called uint32
|
|
|
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
atomic.AddUint32(&called, 1)
|
2016-12-20 22:37:11 +00:00
|
|
|
assert.Equal(t, "POST", req.Method)
|
2016-12-20 00:14:03 +00:00
|
|
|
|
2016-12-20 20:53:26 +00:00
|
|
|
body := &authRequest{}
|
|
|
|
err := json.NewDecoder(req.Body).Decode(body)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, "Reject", body.Test)
|
|
|
|
|
2016-12-20 00:14:03 +00:00
|
|
|
actual := req.Header.Get("Authorization")
|
|
|
|
expected := "Basic " + strings.TrimSpace(
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("user:pass")),
|
|
|
|
)
|
|
|
|
|
2016-12-20 22:37:11 +00:00
|
|
|
w.Header().Set("Lfs-Authenticate", "Basic")
|
2016-12-20 00:14:03 +00:00
|
|
|
if actual != expected {
|
2016-12-23 03:06:51 +00:00
|
|
|
// Write http.StatusUnauthorized to force the credential
|
2016-12-20 00:14:03 +00:00
|
|
|
// helper to reject the credentials
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
defer srv.Close()
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
invalidCreds := creds.Creds(map[string]string{
|
2016-12-20 00:14:03 +00:00
|
|
|
"username": "user",
|
|
|
|
"password": "wrong_pass",
|
|
|
|
"path": "",
|
|
|
|
"protocol": "http",
|
|
|
|
"host": srv.Listener.Addr().String(),
|
|
|
|
})
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
cred := newMockCredentialHelper()
|
|
|
|
cred.Approve(invalidCreds)
|
|
|
|
assert.True(t, cred.IsApproved(invalidCreds))
|
2016-12-20 00:14:03 +00:00
|
|
|
|
2017-10-25 21:33:20 +00:00
|
|
|
c, _ := NewClient(nil)
|
2018-09-19 15:05:48 +00:00
|
|
|
c.Credentials = cred
|
2018-10-25 21:30:45 +00:00
|
|
|
c.Endpoints = NewEndpointFinder(lfshttp.NewContext(git.NewReadOnlyConfig("", ""),
|
|
|
|
nil, map[string]string{
|
|
|
|
"lfs.url": srv.URL,
|
|
|
|
},
|
|
|
|
))
|
2016-12-20 00:14:03 +00:00
|
|
|
|
2016-12-20 22:37:11 +00:00
|
|
|
req, err := http.NewRequest("POST", srv.URL, nil)
|
2016-12-20 00:14:03 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
|
2016-12-20 21:03:00 +00:00
|
|
|
err = MarshalToRequest(req, &authRequest{Test: "Reject"})
|
2016-12-20 00:14:03 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
|
2018-10-02 23:47:10 +00:00
|
|
|
res, err := c.DoWithAuth("", c.Endpoints.AccessFor(srv.URL), req)
|
2016-12-20 00:14:03 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
2018-09-19 15:05:48 +00:00
|
|
|
assert.False(t, cred.IsApproved(invalidCreds))
|
|
|
|
assert.True(t, cred.IsApproved(creds.Creds(map[string]string{
|
2016-12-20 00:14:03 +00:00
|
|
|
"username": "user",
|
|
|
|
"password": "pass",
|
|
|
|
"path": "",
|
|
|
|
"protocol": "http",
|
|
|
|
"host": srv.Listener.Addr().String(),
|
|
|
|
})))
|
|
|
|
assert.EqualValues(t, 3, called)
|
|
|
|
}
|
|
|
|
|
2018-10-02 23:47:10 +00:00
|
|
|
func TestDoWithAuthNoRetry(t *testing.T) {
|
2018-09-27 23:46:56 +00:00
|
|
|
var called uint32
|
|
|
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
atomic.AddUint32(&called, 1)
|
|
|
|
assert.Equal(t, "POST", req.Method)
|
|
|
|
|
|
|
|
body := &authRequest{}
|
|
|
|
err := json.NewDecoder(req.Body).Decode(body)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, "Approve", body.Test)
|
|
|
|
|
|
|
|
w.Header().Set("Lfs-Authenticate", "Basic")
|
|
|
|
actual := req.Header.Get("Authorization")
|
|
|
|
if len(actual) == 0 {
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := "Basic " + strings.TrimSpace(
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("user:pass")),
|
|
|
|
)
|
|
|
|
assert.Equal(t, expected, actual)
|
|
|
|
}))
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
creds := newMockCredentialHelper()
|
2018-10-25 21:30:45 +00:00
|
|
|
c, err := NewClient(lfshttp.NewContext(git.NewReadOnlyConfig("", ""),
|
|
|
|
nil, map[string]string{
|
|
|
|
"lfs.url": srv.URL + "/repo/lfs",
|
|
|
|
},
|
|
|
|
))
|
2018-09-27 23:46:56 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
c.Credentials = creds
|
|
|
|
|
|
|
|
assert.Equal(t, NoneAccess, c.Endpoints.AccessFor(srv.URL+"/repo/lfs").mode)
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", srv.URL+"/repo/lfs/foo", nil)
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
err = MarshalToRequest(req, &authRequest{Test: "Approve"})
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
2018-10-02 23:47:10 +00:00
|
|
|
res, err := c.DoWithAuthNoRetry("", c.Endpoints.AccessFor(srv.URL+"/repo/lfs"), req)
|
2018-09-27 23:46:56 +00:00
|
|
|
assert.True(t, errors.IsAuthError(err))
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, res.StatusCode)
|
|
|
|
assert.Equal(t, BasicAccess, c.Endpoints.AccessFor(srv.URL+"/repo/lfs").mode)
|
|
|
|
assert.EqualValues(t, 1, called)
|
|
|
|
}
|
|
|
|
|
2018-09-24 23:45:32 +00:00
|
|
|
func TestDoAPIRequestWithAuth(t *testing.T) {
|
|
|
|
var called uint32
|
|
|
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
atomic.AddUint32(&called, 1)
|
|
|
|
assert.Equal(t, "POST", req.Method)
|
|
|
|
|
|
|
|
body := &authRequest{}
|
|
|
|
err := json.NewDecoder(req.Body).Decode(body)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, "Approve", body.Test)
|
|
|
|
|
|
|
|
w.Header().Set("Lfs-Authenticate", "Basic")
|
|
|
|
actual := req.Header.Get("Authorization")
|
|
|
|
if len(actual) == 0 {
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := "Basic " + strings.TrimSpace(
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("user:pass")),
|
|
|
|
)
|
|
|
|
assert.Equal(t, expected, actual)
|
|
|
|
}))
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
cred := newMockCredentialHelper()
|
2018-10-25 21:30:45 +00:00
|
|
|
c, err := NewClient(lfshttp.NewContext(git.NewReadOnlyConfig("", ""),
|
|
|
|
nil, map[string]string{
|
|
|
|
"lfs.url": srv.URL + "/repo/lfs",
|
|
|
|
},
|
|
|
|
))
|
2018-09-24 23:45:32 +00:00
|
|
|
require.Nil(t, err)
|
|
|
|
c.Credentials = cred
|
|
|
|
|
|
|
|
assert.Equal(t, NoneAccess, c.Endpoints.AccessFor(srv.URL+"/repo/lfs").mode)
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", srv.URL+"/repo/lfs/foo", nil)
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
err = MarshalToRequest(req, &authRequest{Test: "Approve"})
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
res, err := c.DoAPIRequestWithAuth("", req)
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
|
|
assert.True(t, cred.IsApproved(creds.Creds(map[string]string{
|
|
|
|
"username": "user",
|
|
|
|
"password": "pass",
|
|
|
|
"protocol": "http",
|
|
|
|
"host": srv.Listener.Addr().String(),
|
|
|
|
})))
|
|
|
|
assert.Equal(t, BasicAccess, c.Endpoints.AccessFor(srv.URL+"/repo/lfs").mode)
|
|
|
|
assert.EqualValues(t, 2, called)
|
|
|
|
}
|
|
|
|
|
2016-12-20 00:14:03 +00:00
|
|
|
type mockCredentialHelper struct {
|
2018-09-19 15:05:48 +00:00
|
|
|
Approved map[string]creds.Creds
|
2016-12-20 00:14:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func newMockCredentialHelper() *mockCredentialHelper {
|
|
|
|
return &mockCredentialHelper{
|
2018-09-19 15:05:48 +00:00
|
|
|
Approved: make(map[string]creds.Creds),
|
2016-12-20 00:14:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
func (m *mockCredentialHelper) Fill(input creds.Creds) (creds.Creds, error) {
|
2016-12-20 00:14:03 +00:00
|
|
|
if found, ok := m.Approved[credsToKey(input)]; ok {
|
|
|
|
return found, nil
|
|
|
|
}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
output := make(creds.Creds)
|
2016-12-20 00:14:03 +00:00
|
|
|
for key, value := range input {
|
|
|
|
output[key] = value
|
|
|
|
}
|
|
|
|
if _, ok := output["username"]; !ok {
|
|
|
|
output["username"] = "user"
|
|
|
|
}
|
|
|
|
output["password"] = "pass"
|
|
|
|
return output, nil
|
|
|
|
}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
func (m *mockCredentialHelper) Approve(creds creds.Creds) error {
|
2016-12-20 00:14:03 +00:00
|
|
|
m.Approved[credsToKey(creds)] = creds
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
func (m *mockCredentialHelper) Reject(creds creds.Creds) error {
|
2016-12-20 00:14:03 +00:00
|
|
|
delete(m.Approved, credsToKey(creds))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
func (m *mockCredentialHelper) IsApproved(creds creds.Creds) bool {
|
2016-12-20 00:14:03 +00:00
|
|
|
if found, ok := m.Approved[credsToKey(creds)]; ok {
|
|
|
|
return found["password"] == creds["password"]
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
func credsToKey(creds creds.Creds) string {
|
2016-12-20 00:14:03 +00:00
|
|
|
var kvs []string
|
|
|
|
for _, k := range []string{"protocol", "host", "path"} {
|
|
|
|
kvs = append(kvs, fmt.Sprintf("%s:%s", k, creds[k]))
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join(kvs, " ")
|
|
|
|
}
|
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
func basicAuth(user, pass string) string {
|
|
|
|
value := fmt.Sprintf("%s:%s", user, pass)
|
|
|
|
return fmt.Sprintf("Basic %s", strings.TrimSpace(base64.StdEncoding.EncodeToString([]byte(value))))
|
|
|
|
}
|
|
|
|
|
|
|
|
type getCredsExpected struct {
|
2018-09-20 17:21:42 +00:00
|
|
|
Access AccessMode
|
2018-09-19 15:05:48 +00:00
|
|
|
Creds creds.Creds
|
2016-12-23 03:06:51 +00:00
|
|
|
CredsURL string
|
2016-12-19 21:38:06 +00:00
|
|
|
Authorization string
|
|
|
|
}
|
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
type getCredsTest struct {
|
|
|
|
Remote string
|
|
|
|
Method string
|
|
|
|
Href string
|
2018-09-24 23:45:32 +00:00
|
|
|
Endpoint string
|
2016-12-23 03:06:51 +00:00
|
|
|
Header map[string]string
|
|
|
|
Config map[string]string
|
|
|
|
Expected getCredsExpected
|
2016-12-19 21:38:06 +00:00
|
|
|
}
|
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
func TestGetCreds(t *testing.T) {
|
|
|
|
tests := map[string]getCredsTest{
|
|
|
|
"no access": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2016-12-20 00:14:03 +00:00
|
|
|
Config: map[string]string{
|
2016-12-23 03:06:51 +00:00
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
2018-09-24 23:45:32 +00:00
|
|
|
Access: NoneAccess,
|
2016-12-20 00:14:03 +00:00
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"basic access": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2016-12-20 00:14:03 +00:00
|
|
|
Config: map[string]string{
|
2016-12-23 03:06:51 +00:00
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
},
|
2017-10-26 22:29:43 +00:00
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("git-server.com", "monkey"),
|
|
|
|
CredsURL: "https://git-server.com/repo/lfs",
|
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "https",
|
|
|
|
"host": "git-server.com",
|
|
|
|
"username": "git-server.com",
|
|
|
|
"password": "monkey",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"basic access with usehttppath": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2017-10-26 22:29:43 +00:00
|
|
|
Config: map[string]string{
|
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
"credential.usehttppath": "true",
|
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("git-server.com", "monkey"),
|
|
|
|
CredsURL: "https://git-server.com/repo/lfs",
|
2017-10-27 16:42:17 +00:00
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "https",
|
|
|
|
"host": "git-server.com",
|
|
|
|
"username": "git-server.com",
|
|
|
|
"password": "monkey",
|
|
|
|
"path": "repo/lfs",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"basic access with url-specific usehttppath": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2017-10-27 16:42:17 +00:00
|
|
|
Config: map[string]string{
|
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
"credential.https://git-server.com.usehttppath": "true",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("git-server.com", "monkey"),
|
|
|
|
CredsURL: "https://git-server.com/repo/lfs",
|
2016-12-23 03:06:51 +00:00
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "https",
|
|
|
|
"host": "git-server.com",
|
|
|
|
"username": "git-server.com",
|
|
|
|
"password": "monkey",
|
|
|
|
"path": "repo/lfs",
|
|
|
|
},
|
2016-12-20 00:14:03 +00:00
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2017-01-05 21:13:27 +00:00
|
|
|
"ntlm": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2017-01-05 21:13:27 +00:00
|
|
|
Config: map[string]string{
|
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "ntlm",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: NTLMAccess,
|
|
|
|
CredsURL: "https://git-server.com/repo/lfs",
|
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "https",
|
|
|
|
"host": "git-server.com",
|
|
|
|
"username": "git-server.com",
|
|
|
|
"password": "monkey",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"custom auth": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2016-12-23 03:06:51 +00:00
|
|
|
Header: map[string]string{
|
|
|
|
"Authorization": "custom",
|
|
|
|
},
|
|
|
|
Config: map[string]string{
|
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: "custom",
|
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"username in url": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2016-12-20 00:14:03 +00:00
|
|
|
Config: map[string]string{
|
2016-12-23 03:06:51 +00:00
|
|
|
"lfs.url": "https://user@git-server.com/repo/lfs",
|
2016-12-23 17:42:57 +00:00
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
2016-12-23 03:06:51 +00:00
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("user", "monkey"),
|
|
|
|
CredsURL: "https://user@git-server.com/repo/lfs",
|
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "https",
|
|
|
|
"host": "git-server.com",
|
|
|
|
"username": "user",
|
|
|
|
"password": "monkey",
|
|
|
|
},
|
2016-12-20 00:14:03 +00:00
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"different remote url, basic access": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2016-12-20 00:14:03 +00:00
|
|
|
Config: map[string]string{
|
2016-12-23 03:06:51 +00:00
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
"remote.origin.url": "https://git-server.com/repo",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("git-server.com", "monkey"),
|
|
|
|
CredsURL: "https://git-server.com/repo",
|
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "https",
|
|
|
|
"host": "git-server.com",
|
|
|
|
"username": "git-server.com",
|
|
|
|
"password": "monkey",
|
|
|
|
},
|
2016-12-20 00:14:03 +00:00
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"api url auth": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo",
|
2016-12-20 00:14:03 +00:00
|
|
|
Config: map[string]string{
|
2018-08-28 15:25:44 +00:00
|
|
|
"lfs.url": "https://user:pass@git-server.com/repo",
|
2016-12-23 17:42:57 +00:00
|
|
|
"lfs.https://git-server.com/repo.access": "basic",
|
2016-12-23 03:06:51 +00:00
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("user", "pass"),
|
2016-12-20 00:14:03 +00:00
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"git url auth": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com/repo/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo",
|
2016-12-23 03:06:51 +00:00
|
|
|
Config: map[string]string{
|
2018-08-28 15:25:44 +00:00
|
|
|
"lfs.url": "https://git-server.com/repo",
|
2016-12-23 03:06:51 +00:00
|
|
|
"lfs.https://git-server.com/repo.access": "basic",
|
|
|
|
"remote.origin.url": "https://user:pass@git-server.com/repo",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("user", "pass"),
|
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"scheme mismatch": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "http://git-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2016-12-19 21:38:06 +00:00
|
|
|
Config: map[string]string{
|
2016-12-23 03:06:51 +00:00
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("git-server.com", "monkey"),
|
|
|
|
CredsURL: "http://git-server.com/repo/lfs/locks",
|
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "http",
|
|
|
|
"host": "git-server.com",
|
|
|
|
"username": "git-server.com",
|
|
|
|
"password": "monkey",
|
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"host mismatch": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://lfs-server.com/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2016-12-20 00:14:03 +00:00
|
|
|
Config: map[string]string{
|
2016-12-23 03:06:51 +00:00
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("lfs-server.com", "monkey"),
|
|
|
|
CredsURL: "https://lfs-server.com/repo/lfs/locks",
|
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "https",
|
|
|
|
"host": "lfs-server.com",
|
|
|
|
"username": "lfs-server.com",
|
|
|
|
"password": "monkey",
|
|
|
|
},
|
2016-12-20 00:14:03 +00:00
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2016-12-23 03:06:51 +00:00
|
|
|
"port mismatch": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "GET",
|
|
|
|
Href: "https://git-server.com:8080/repo/lfs/locks",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2016-12-23 03:06:51 +00:00
|
|
|
Config: map[string]string{
|
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("git-server.com:8080", "monkey"),
|
|
|
|
CredsURL: "https://git-server.com:8080/repo/lfs/locks",
|
|
|
|
Creds: map[string]string{
|
|
|
|
"protocol": "https",
|
|
|
|
"host": "git-server.com:8080",
|
|
|
|
"username": "git-server.com:8080",
|
|
|
|
"password": "monkey",
|
|
|
|
},
|
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
},
|
2017-03-28 16:16:12 +00:00
|
|
|
"bare ssh URI": getCredsTest{
|
2018-09-24 23:45:32 +00:00
|
|
|
Remote: "origin",
|
|
|
|
Method: "POST",
|
|
|
|
Href: "https://git-server.com/repo/lfs/objects/batch",
|
|
|
|
Endpoint: "https://git-server.com/repo/lfs",
|
2017-03-28 16:16:12 +00:00
|
|
|
Config: map[string]string{
|
|
|
|
"lfs.url": "https://git-server.com/repo/lfs",
|
|
|
|
"lfs.https://git-server.com/repo/lfs.access": "basic",
|
|
|
|
|
|
|
|
"remote.origin.url": "git@git-server.com:repo.git",
|
|
|
|
},
|
|
|
|
Expected: getCredsExpected{
|
|
|
|
Access: BasicAccess,
|
|
|
|
Authorization: basicAuth("git-server.com", "monkey"),
|
|
|
|
CredsURL: "https://git-server.com/repo/lfs",
|
|
|
|
Creds: map[string]string{
|
|
|
|
"host": "git-server.com",
|
|
|
|
"password": "monkey",
|
|
|
|
"protocol": "https",
|
|
|
|
"username": "git-server.com",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-12-19 21:38:06 +00:00
|
|
|
}
|
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
for desc, test := range tests {
|
|
|
|
t.Log(desc)
|
|
|
|
req, err := http.NewRequest(test.Method, test.Href, nil)
|
2016-12-19 21:38:06 +00:00
|
|
|
if err != nil {
|
2016-12-23 03:06:51 +00:00
|
|
|
t.Errorf("[%s] %s", desc, err)
|
2016-12-19 21:38:06 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
for key, value := range test.Header {
|
2016-12-19 21:38:06 +00:00
|
|
|
req.Header.Set(key, value)
|
|
|
|
}
|
|
|
|
|
2018-10-25 21:30:45 +00:00
|
|
|
ctx := lfshttp.NewContext(git.NewReadOnlyConfig("", ""), nil, test.Config)
|
2017-10-26 22:29:43 +00:00
|
|
|
client, _ := NewClient(ctx)
|
|
|
|
client.Credentials = &fakeCredentialFiller{}
|
|
|
|
client.Endpoints = NewEndpointFinder(ctx)
|
2019-04-18 14:41:55 +00:00
|
|
|
credWrapper, err := client.getCreds(test.Remote, client.Endpoints.AccessFor(test.Endpoint), req)
|
2016-12-23 03:06:51 +00:00
|
|
|
if !assert.Nil(t, err) {
|
2016-12-19 21:38:06 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-09-24 23:45:32 +00:00
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
assert.Equal(t, test.Expected.Authorization, req.Header.Get("Authorization"), "authorization")
|
2016-12-19 21:38:06 +00:00
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
if test.Expected.Creds != nil {
|
2019-04-18 14:41:55 +00:00
|
|
|
assert.EqualValues(t, test.Expected.Creds, credWrapper.Creds)
|
2016-12-19 21:38:06 +00:00
|
|
|
} else {
|
2019-04-18 14:41:55 +00:00
|
|
|
assert.Nil(t, credWrapper.Creds, "creds")
|
2016-12-19 21:38:06 +00:00
|
|
|
}
|
|
|
|
|
2016-12-23 03:06:51 +00:00
|
|
|
if len(test.Expected.CredsURL) > 0 {
|
2019-04-18 14:41:55 +00:00
|
|
|
if assert.NotNil(t, credWrapper.Url, "credURL") {
|
|
|
|
assert.Equal(t, test.Expected.CredsURL, credWrapper.Url.String(), "credURL")
|
2016-12-19 21:38:06 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-04-18 14:41:55 +00:00
|
|
|
assert.Nil(t, credWrapper.Url)
|
2016-12-19 21:38:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type fakeCredentialFiller struct{}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
func (f *fakeCredentialFiller) Fill(input creds.Creds) (creds.Creds, error) {
|
|
|
|
output := make(creds.Creds)
|
2016-12-19 21:38:06 +00:00
|
|
|
for key, value := range input {
|
|
|
|
output[key] = value
|
|
|
|
}
|
|
|
|
if _, ok := output["username"]; !ok {
|
|
|
|
output["username"] = input["host"]
|
|
|
|
}
|
|
|
|
output["password"] = "monkey"
|
|
|
|
return output, nil
|
|
|
|
}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
func (f *fakeCredentialFiller) Approve(creds creds.Creds) error {
|
2016-12-19 21:38:06 +00:00
|
|
|
return errors.New("Not implemented")
|
|
|
|
}
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
func (f *fakeCredentialFiller) Reject(creds creds.Creds) error {
|
2016-12-19 21:38:06 +00:00
|
|
|
return errors.New("Not implemented")
|
|
|
|
}
|
2018-09-06 21:42:41 +00:00
|
|
|
|
2018-09-11 21:14:37 +00:00
|
|
|
func TestClientRedirectReauthenticate(t *testing.T) {
|
|
|
|
var srv1, srv2 *httptest.Server
|
|
|
|
var called1, called2 uint32
|
2018-09-19 15:05:48 +00:00
|
|
|
var creds1, creds2 creds.Creds
|
2018-09-11 21:14:37 +00:00
|
|
|
|
|
|
|
srv1 = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
atomic.AddUint32(&called1, 1)
|
|
|
|
|
|
|
|
if hdr := r.Header.Get("Authorization"); len(hdr) > 0 {
|
|
|
|
parts := strings.SplitN(hdr, " ", 2)
|
|
|
|
typ, b64 := parts[0], parts[1]
|
|
|
|
|
|
|
|
auth, err := base64.URLEncoding.DecodeString(b64)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, "Basic", typ)
|
|
|
|
assert.Equal(t, "user1:pass1", string(auth))
|
|
|
|
|
|
|
|
http.Redirect(w, r, srv2.URL+r.URL.Path, http.StatusMovedPermanently)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
}))
|
|
|
|
|
|
|
|
srv2 = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
atomic.AddUint32(&called2, 1)
|
|
|
|
|
|
|
|
parts := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
|
|
|
typ, b64 := parts[0], parts[1]
|
|
|
|
|
|
|
|
auth, err := base64.URLEncoding.DecodeString(b64)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, "Basic", typ)
|
|
|
|
assert.Equal(t, "user2:pass2", string(auth))
|
|
|
|
}))
|
|
|
|
|
|
|
|
// Change the URL of srv2 to make it appears as if it is a different
|
|
|
|
// host.
|
|
|
|
srv2.URL = strings.Replace(srv2.URL, "127.0.0.1", "0.0.0.0", 1)
|
|
|
|
|
2018-09-19 15:05:48 +00:00
|
|
|
creds1 = creds.Creds(map[string]string{
|
2018-09-11 21:14:37 +00:00
|
|
|
"protocol": "http",
|
|
|
|
"host": strings.TrimPrefix(srv1.URL, "http://"),
|
|
|
|
|
|
|
|
"username": "user1",
|
|
|
|
"password": "pass1",
|
|
|
|
})
|
2018-09-19 15:05:48 +00:00
|
|
|
creds2 = creds.Creds(map[string]string{
|
2018-09-11 21:14:37 +00:00
|
|
|
"protocol": "http",
|
|
|
|
"host": strings.TrimPrefix(srv2.URL, "http://"),
|
|
|
|
|
|
|
|
"username": "user2",
|
|
|
|
"password": "pass2",
|
|
|
|
})
|
|
|
|
|
|
|
|
defer srv1.Close()
|
|
|
|
defer srv2.Close()
|
|
|
|
|
|
|
|
c, err := NewClient(lfshttp.NewContext(nil, nil, nil))
|
2018-09-19 15:05:48 +00:00
|
|
|
cred := creds.NewCredentialCacher()
|
|
|
|
cred.Approve(creds1)
|
|
|
|
cred.Approve(creds2)
|
|
|
|
c.Credentials = cred
|
2018-09-11 21:14:37 +00:00
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", srv1.URL, nil)
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
2018-09-24 23:45:32 +00:00
|
|
|
_, err = c.DoAPIRequestWithAuth("", req)
|
2018-09-11 21:14:37 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
|
|
|
|
// called1 is 2 since LFS tries an unauthenticated request first
|
|
|
|
assert.EqualValues(t, 2, called1)
|
|
|
|
assert.EqualValues(t, 1, called2)
|
|
|
|
}
|