From 28957606ae864273b5470f82f7555e45e1815fc1 Mon Sep 17 00:00:00 2001 From: reggie-k Date: Wed, 29 Jan 2025 18:05:30 +0200 Subject: [PATCH] added passing the bearer header Signed-off-by: reggie-k --- util/git/client.go | 4 ++ util/git/client_test.go | 91 +++++++++++++++++++++++++++++++++++++++++ util/git/creds.go | 13 ++++++ util/git/creds_test.go | 21 ++++++++++ 4 files changed, 129 insertions(+) diff --git a/util/git/client.go b/util/git/client.go index c23b4c5d4c5e1..27ccbb7e7b110 100644 --- a/util/git/client.go +++ b/util/git/client.go @@ -971,6 +971,10 @@ func (m *nativeGitClient) runCredentialedCmd(args ...string) error { for _, e := range environ { if strings.HasPrefix(e, forceBasicAuthHeaderEnv+"=") { args = append([]string{"--config-env", "http.extraHeader=" + forceBasicAuthHeaderEnv}, args...) + } else { + if strings.HasPrefix(e, bearerAuthHeaderEnv+"=") { + args = append([]string{"--config-env", "http.extraHeader=" + bearerAuthHeaderEnv}, args...) + } } } diff --git a/util/git/client_test.go b/util/git/client_test.go index be2584a98a3ba..93593bb12c375 100644 --- a/util/git/client_test.go +++ b/util/git/client_test.go @@ -1,6 +1,9 @@ package git import ( + "context" + "errors" + "io" "os" "os/exec" "path" @@ -904,3 +907,91 @@ func TestNewAuth(t *testing.T) { }) } } +func Test_nativeGitClient_runCredentialedCmd(t *testing.T) { + tests := []struct { + name string + creds Creds + environ []string + expectedArgs []string + expectedEnv []string + expectedErr bool + }{ + { + name: "basic auth header set", + creds: &mockCreds{ + environ: []string{forceBasicAuthHeaderEnv + "=Basic dGVzdDp0ZXN0"}, + }, + expectedArgs: []string{"--config-env", "http.extraHeader=" + forceBasicAuthHeaderEnv, "status"}, + expectedEnv: []string{forceBasicAuthHeaderEnv + "=Basic dGVzdDp0ZXN0"}, + expectedErr: false, + }, + { + name: "bearer auth header set", + creds: &mockCreds{ + environ: []string{bearerAuthHeaderEnv + "=Bearer test-token"}, + }, + expectedArgs: []string{"--config-env", "http.extraHeader=" + bearerAuthHeaderEnv, "status"}, + expectedEnv: []string{bearerAuthHeaderEnv + "=Bearer test-token"}, + expectedErr: false, + }, + { + name: "no auth header set", + creds: &mockCreds{ + environ: []string{}, + }, + expectedArgs: []string{"status"}, + expectedEnv: []string{}, + expectedErr: false, + }, + { + name: "error getting environment", + creds: &mockCreds{ + environErr: true, + }, + expectedArgs: []string{}, + expectedEnv: []string{}, + expectedErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client := &nativeGitClient{ + creds: tt.creds, + } + + err := client.runCredentialedCmd("status") + if (err != nil) != tt.expectedErr { + t.Errorf("runCredentialedCmd() error = %v, expectedErr %v", err, tt.expectedErr) + return + } + + if tt.expectedErr { + return + } + + cmd := exec.Command("git", tt.expectedArgs...) + cmd.Env = append(os.Environ(), tt.expectedEnv...) + output, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("runCredentialedCmd() command error = %v, output = %s", err, output) + } + }) + } +} + +type mockCreds struct { + environ []string + environErr bool +} + +func (m *mockCreds) Environ() (io.Closer, []string, error) { + if m.environErr { + return nil, nil, errors.New("error getting environment") + } + return io.NopCloser(nil), m.environ, nil +} + +func (m *mockCreds) GetUserInfo(ctx context.Context) (string, string, error) { + return "", "", nil +} diff --git a/util/git/creds.go b/util/git/creds.go index 30d61889a28cd..413006b92554c 100644 --- a/util/git/creds.go +++ b/util/git/creds.go @@ -49,6 +49,7 @@ const ( // githubAccessTokenUsername is a username that is used to with the github access token githubAccessTokenUsername = "x-access-token" forceBasicAuthHeaderEnv = "ARGOCD_GIT_AUTH_HEADER" + bearerAuthHeaderEnv = "ARGOCD_GIT_BEARER_AUTH_HEADER" // This is the resource id of the OAuth application of Azure Devops. azureDevopsEntraResourceId = "499b84ac-1321-427f-aa17-267ca6975798/.default" ) @@ -179,6 +180,13 @@ func (creds HTTPSCreds) BasicAuthHeader() string { return h } +func (creds HTTPSCreds) BearerAuthHeader() string { + h := "Authorization: Bearer " + t := creds.bearerToken + h += base64.StdEncoding.EncodeToString([]byte(t)) + return h +} + // Get additional required environment variables for executing git client to // access specific repository via HTTPS. func (creds HTTPSCreds) Environ() (io.Closer, []string, error) { @@ -240,6 +248,11 @@ func (creds HTTPSCreds) Environ() (io.Closer, []string, error) { // skipped. This is insecure, but some environments may need it. if creds.password != "" && creds.forceBasicAuth { env = append(env, fmt.Sprintf("%s=%s", forceBasicAuthHeaderEnv, creds.BasicAuthHeader())) + } else { + // If bearer token is set, we will set ARGOCD_BEARER_AUTH_HEADER to hold the HTTP authorization header + if creds.bearerToken != "" { + env = append(env, fmt.Sprintf("%s=%s", bearerAuthHeaderEnv, creds.BearerAuthHeader())) + } } nonce := creds.store.Add(text.FirstNonEmpty(creds.username, githubAccessTokenUsername), creds.password) env = append(env, creds.store.Environ(nonce)...) diff --git a/util/git/creds_test.go b/util/git/creds_test.go index 4219b0a763f51..9b90761c8390c 100644 --- a/util/git/creds_test.go +++ b/util/git/creds_test.go @@ -167,6 +167,27 @@ func TestHTTPSCreds_Environ_forceBasicAuth(t *testing.T) { }) } +func TestHTTPSCreds_Environ_bearerTokenAuth(t *testing.T) { + t.Run("Enabled and credentials set", func(t *testing.T) { + store := &memoryCredsStore{creds: make(map[string]cred)} + creds := NewHTTPSCreds("", "", "token", "", "", false, "", "", store, false) + closer, env, err := creds.Environ() + require.NoError(t, err) + defer closer.Close() + var header string + for _, envVar := range env { + if strings.HasPrefix(envVar, bearerAuthHeaderEnv+"=") { + header = envVar[len(bearerAuthHeaderEnv)+1:] + } + if header != "" { + break + } + } + b64enc := base64.StdEncoding.EncodeToString([]byte("token")) + assert.Equal(t, "Authorization: Bearer "+b64enc, header) + }) +} + func TestHTTPSCreds_Environ_clientCert(t *testing.T) { store := &memoryCredsStore{creds: make(map[string]cred)} creds := NewHTTPSCreds("", "", "", "clientCertData", "clientCertKey", false, "", "", store, false)