diff --git a/pkg/agent/tunnelserver/tunnelserver.go b/pkg/agent/tunnelserver/tunnelserver.go index 0505a54bb..bafc2485d 100644 --- a/pkg/agent/tunnelserver/tunnelserver.go +++ b/pkg/agent/tunnelserver/tunnelserver.go @@ -214,6 +214,21 @@ func (t *tunnelServer) GitCredentials(ctx context.Context, message *tunnel.Messa credentials.Username = t.gitCredentialsOverride.username credentials.Password = t.gitCredentialsOverride.token } else { + if t.workspace.Source.GitRepository != "" { + path, err := gitcredentials.GetHTTPPath(ctx, gitcredentials.GetHttpPathParameters{ + Host: credentials.Host, + Protocol: credentials.Protocol, + CurrentPath: credentials.Path, + Repository: t.workspace.Source.GitRepository, + }) + if err != nil { + return nil, fmt.Errorf("get http path: %w", err) + } + // Set the credentials `path` field to the path component of the git repository URL. + // This allows downstream credential helpers to figure out which passwords needs to be fetched + credentials.Path = path + } + response, err := gitcredentials.GetCredentials(credentials) if err != nil { return nil, perrors.Wrap(err, "get git response") diff --git a/pkg/gitcredentials/gitcredentials.go b/pkg/gitcredentials/gitcredentials.go index 362745b70..23e7cc073 100644 --- a/pkg/gitcredentials/gitcredentials.go +++ b/pkg/gitcredentials/gitcredentials.go @@ -3,6 +3,7 @@ package gitcredentials import ( "context" "fmt" + netUrl "net/url" "os" "os/exec" "path/filepath" @@ -224,6 +225,41 @@ func GetCredentials(requestObj *GitCredentials) (*GitCredentials, error) { return Parse(string(stdout)) } +type GetHttpPathParameters struct { + Host string + Protocol string + CurrentPath string + Repository string +} + +// GetHTTPPath checks for gits `credential.useHttpPath` setting for a given host+protocol and returns the path component +// of `GitCredential` if the setting is true +func GetHTTPPath(ctx context.Context, params GetHttpPathParameters) (string, error) { + // No need to look up the HTTP Path if we already have one + if params.CurrentPath != "" { + return params.CurrentPath, nil + } + + // Check if we need to respect gits `credential.useHttpPath` + // The actual format for the key is `credential.$PROTOCOL://$HOST.useHttpPath`, i.e. `credential.https://github.com.useHttpPath` + configKey := fmt.Sprintf("credential.%s://%s.useHttpPath", params.Protocol, params.Host) + out, err := git.CommandContext(ctx, "config", "--get", configKey).Output() + if err != nil { + return "", fmt.Errorf("inspect useHttpPath for host %s: %w", params.Host, err) + } + if strings.TrimSpace(string(out)) != "true" { + return "", nil + } + // We can assume the GitRepository is always HTTP(S) based as otherwise we wouldn't + // request credentials for it + url, err := netUrl.Parse(params.Repository) + if err != nil { + return "", fmt.Errorf("parse workspace repository: %w", err) + } + + return url.Path, nil +} + func SetupGpgGitKey(gitSignKey string) error { gitConfigCmd := exec.Command("git", []string{"config", "--global", "user.signingKey", gitSignKey}...)