diff --git a/bin/core/src/helpers/mod.rs b/bin/core/src/helpers/mod.rs index 1fe282fb2a..dd46df3b9e 100644 --- a/bin/core/src/helpers/mod.rs +++ b/bin/core/src/helpers/mod.rs @@ -55,7 +55,14 @@ pub fn empty_or_only_spaces(word: &str) -> bool { /// First checks db for token, then checks core config. /// Only errors if db call errors. -/// Returns (token, use_https) +/// Returns the configured account credential prepared for HTTPS basic auth. +/// +/// The returned value is formatted as `":"` so that +/// [`RepoExecutionArgs::remote_url`] produces +/// `https://:@host/...`. If the stored token already +/// contains a `:`, the user has manually combined the credentials +/// (the v1.19.2 workaround for issue #387) and we pass it through +/// unchanged for backwards compatibility. pub async fn git_token( provider_domain: &str, account_username: &str, @@ -71,7 +78,10 @@ pub async fn git_token( .context("failed to query db for git provider accounts")?; if let Some(provider) = db_provider { on_https_found(provider.https); - return Ok(Some(provider.token)); + return Ok(Some(combine_account_credential( + account_username, + &provider.token, + ))); } Ok( core_config() @@ -84,11 +94,32 @@ pub async fn git_token( .accounts .iter() .find(|account| account.username == account_username) - .map(|account| account.token.clone()) + .map(|account| { + combine_account_credential(account_username, &account.token) + }) }), ) } +/// Combine a configured git account `username` with its `token` into the +/// `":"` form that [`RepoExecutionArgs::remote_url`] +/// splits back out for HTTPS basic auth. +/// +/// Without this, [`RepoExecutionArgs::remote_url`] would fall back to its +/// hardcoded `token:@` default and clones against providers that +/// validate the basic-auth username (GitLab deploy tokens, Bitbucket, +/// fine-grained GitHub PATs) would fail with `HTTP Basic: Access denied`. +/// +/// If the stored token already contains a `:` the user has manually +/// combined the credentials (the v1.19.2 workaround for issue #387); +/// return it unchanged so existing setups keep working. +fn combine_account_credential(username: &str, token: &str) -> String { + if token.contains(':') { + return token.to_string(); + } + format!("{username}:{token}") +} + pub async fn stack_git_token( stack: &mut Stack, repo: Option<&mut Repo>, diff --git a/bin/periphery/src/helpers.rs b/bin/periphery/src/helpers.rs index a17c052334..2380ae64b6 100644 --- a/bin/periphery/src/helpers.rs +++ b/bin/periphery/src/helpers.rs @@ -238,6 +238,13 @@ pub async fn handle_post_repo_execution( // Token // ======= +/// Look up a raw access token in the Periphery git provider config. +/// +/// Use this when you only need the secret (for example to redact it from +/// log output). For building a clone URL prefer [`git_token`], which also +/// combines the configured username with the token so the resulting URL +/// authenticates correctly against providers that validate the basic-auth +/// username (GitLab deploy tokens, Bitbucket, fine-grained GitHub PATs). pub fn git_token_simple( domain: &str, account_username: &str, @@ -252,6 +259,19 @@ pub fn git_token_simple( .with_context(|| format!("Did not find token in config for git account {account_username} | domain {domain}")) } +/// Resolve the credential to use when running `git clone` / `git fetch` +/// on the Periphery host. +/// +/// If Core sent a token for this execution, use it as-is — Core is +/// responsible for formatting it as `":"` (see +/// `git_token` in the Core helpers). Otherwise fall back to the +/// Periphery config and combine the configured account username with +/// its token in the same form so [`RepoExecutionArgs::remote_url`] +/// produces `https://:@host/...`. +/// +/// Backwards-compatible: if the stored token already contains a `:` +/// the caller used the v1.19.2 workaround for issue #387, so we pass +/// it through unchanged. pub fn git_token( core_token: Option, args: &RepoExecutionArgs, @@ -263,7 +283,10 @@ pub fn git_token( return Ok(None); }; let token = git_token_simple(&args.provider, account)?; - Ok(Some(token.to_string())) + if token.contains(':') { + return Ok(Some(token.to_string())); + } + Ok(Some(format!("{account}:{token}"))) } pub fn registry_token(