diff --git a/CHANGELOG.md b/CHANGELOG.md index 687380a..ecd78f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Support full version requirements in workspace version field by @daimond113 +- Improved authentication system for registry changes by @daimond113 ### Fixed - Correct `pesde.toml` inclusion message in `publish` command by @daimond113 diff --git a/src/cli/auth.rs b/src/cli/auth.rs index 437055e..e940483 100644 --- a/src/cli/auth.rs +++ b/src/cli/auth.rs @@ -1,23 +1,51 @@ use crate::cli::config::{read_config, write_config}; use anyhow::Context; +use gix::bstr::BStr; use keyring::Entry; -use serde::Deserialize; +use reqwest::header::AUTHORIZATION; +use serde::{ser::SerializeMap, Deserialize, Serialize}; +use std::collections::BTreeMap; -pub fn get_token() -> anyhow::Result> { - match std::env::var("PESDE_TOKEN") { - Ok(token) => return Ok(Some(token)), - Err(std::env::VarError::NotPresent) => {} - Err(e) => return Err(e.into()), +#[derive(Debug, Clone)] +pub struct Tokens(pub BTreeMap); + +impl Serialize for Tokens { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut map = serializer.serialize_map(Some(self.0.len()))?; + for (k, v) in &self.0 { + map.serialize_entry(&k.to_bstring().to_string(), v)?; + } + map.end() } +} +impl<'de> Deserialize<'de> for Tokens { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + Ok(Tokens( + BTreeMap::::deserialize(deserializer)? + .into_iter() + .map(|(k, v)| gix::Url::from_bytes(BStr::new(&k)).map(|k| (k, v))) + .collect::>() + .map_err(serde::de::Error::custom)?, + )) + } +} + +pub fn get_tokens() -> anyhow::Result { let config = read_config()?; - if let Some(token) = config.token { - return Ok(Some(token)); + if !config.tokens.0.is_empty() { + return Ok(config.tokens); } - match Entry::new("token", env!("CARGO_PKG_NAME")) { + match Entry::new("tokens", env!("CARGO_PKG_NAME")) { Ok(entry) => match entry.get_password() { - Ok(token) => return Ok(Some(token)), + Ok(token) => return serde_json::from_str(&token).context("failed to parse tokens"), Err(keyring::Error::PlatformFailure(_) | keyring::Error::NoEntry) => {} Err(e) => return Err(e.into()), }, @@ -25,32 +53,32 @@ pub fn get_token() -> anyhow::Result> { Err(e) => return Err(e.into()), } - Ok(None) + Ok(Tokens(BTreeMap::new())) } -pub fn set_token(token: Option<&str>) -> anyhow::Result<()> { - let entry = match Entry::new("token", env!("CARGO_PKG_NAME")) { - Ok(entry) => entry, - Err(e) => return Err(e.into()), - }; +pub fn set_tokens(tokens: Tokens) -> anyhow::Result<()> { + let entry = Entry::new("tokens", env!("CARGO_PKG_NAME"))?; + let json = serde_json::to_string(&tokens).context("failed to serialize tokens")?; - let result = if let Some(token) = token { - entry.set_password(token) - } else { - entry.delete_credential() - }; - - match result { + match entry.set_password(&json) { Ok(()) => return Ok(()), Err(keyring::Error::PlatformFailure(_) | keyring::Error::NoEntry) => {} Err(e) => return Err(e.into()), } let mut config = read_config()?; - config.token = token.map(|s| s.to_string()); - write_config(&config)?; + config.tokens = tokens; + write_config(&config).map_err(Into::into) +} - Ok(()) +pub fn set_token(repo: &gix::Url, token: Option<&str>) -> anyhow::Result<()> { + let mut tokens = get_tokens()?; + if let Some(token) = token { + tokens.0.insert(repo.clone(), token.to_string()); + } else { + tokens.0.remove(repo); + } + set_tokens(tokens) } #[derive(Debug, Deserialize)] @@ -64,7 +92,7 @@ pub fn get_token_login( ) -> anyhow::Result { let response = reqwest .get("https://api.github.com/user") - .header("Authorization", access_token) + .header(AUTHORIZATION, access_token) .send() .context("failed to send user request")? .error_for_status() diff --git a/src/cli/commands/auth/login.rs b/src/cli/commands/auth/login.rs index c430e1d..59cc0c6 100644 --- a/src/cli/commands/auth/login.rs +++ b/src/cli/commands/auth/login.rs @@ -5,24 +5,16 @@ use serde::Deserialize; use url::Url; use pesde::{ - errors::ManifestReadError, source::{pesde::PesdePackageSource, traits::PackageSource}, Project, }; -use crate::cli::{ - auth::{get_token_login, set_token}, - config::read_config, -}; +use crate::cli::auth::{get_token_login, set_token}; #[derive(Debug, Args)] pub struct LoginCommand { - /// The index to use. Defaults to `default`, or the configured default index if current directory doesn't have a manifest - #[arg(short, long)] - index: Option, - /// The token to use for authentication, skipping login - #[arg(short, long, conflicts_with = "index")] + #[arg(short, long)] token: Option, } @@ -55,41 +47,13 @@ enum AccessTokenResponse { impl LoginCommand { pub fn authenticate_device_flow( &self, + index_url: &gix::Url, project: &Project, reqwest: &reqwest::blocking::Client, ) -> anyhow::Result { - let manifest = match project.deser_manifest() { - Ok(manifest) => Some(manifest), - Err(e) => match e { - ManifestReadError::Io(e) if e.kind() == std::io::ErrorKind::NotFound => None, - e => return Err(e.into()), - }, - }; + println!("logging in into {index_url}"); - let index_url = match self.index.as_deref() { - Some(index) => match index.try_into() { - Ok(url) => Some(url), - Err(_) => None, - }, - None => match manifest { - Some(_) => None, - None => Some(read_config()?.default_index), - }, - }; - - let index_url = match index_url { - Some(url) => url, - None => { - let index_name = self.index.as_deref().unwrap_or("default"); - - match manifest.unwrap().indices.get(index_name) { - Some(index) => index.clone(), - None => anyhow::bail!("Index {index_name} not found"), - } - } - }; - - let source = PesdePackageSource::new(index_url); + let source = PesdePackageSource::new(index_url.clone()); source.refresh(project).context("failed to refresh index")?; let config = source @@ -182,24 +146,32 @@ impl LoginCommand { anyhow::bail!("code expired, please re-run the login command"); } - pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> { + pub fn run( + self, + index_url: gix::Url, + project: Project, + reqwest: reqwest::blocking::Client, + ) -> anyhow::Result<()> { let token_given = self.token.is_some(); let token = match self.token { Some(token) => token, - None => self.authenticate_device_flow(&project, &reqwest)?, + None => self.authenticate_device_flow(&index_url, &project, &reqwest)?, }; let token = if token_given { - println!("set token"); + println!("set token for {index_url}"); token } else { let token = format!("Bearer {token}"); - println!("logged in as {}", get_token_login(&reqwest, &token)?.bold()); + println!( + "logged in as {} for {index_url}", + get_token_login(&reqwest, &token)?.bold() + ); token }; - set_token(Some(&token))?; + set_token(&index_url, Some(&token))?; Ok(()) } diff --git a/src/cli/commands/auth/logout.rs b/src/cli/commands/auth/logout.rs index 597ad6d..3c6a155 100644 --- a/src/cli/commands/auth/logout.rs +++ b/src/cli/commands/auth/logout.rs @@ -5,10 +5,10 @@ use clap::Args; pub struct LogoutCommand {} impl LogoutCommand { - pub fn run(self) -> anyhow::Result<()> { - set_token(None)?; + pub fn run(self, index_url: gix::Url) -> anyhow::Result<()> { + set_token(&index_url, None)?; - println!("logged out"); + println!("logged out of {index_url}"); Ok(()) } diff --git a/src/cli/commands/auth/mod.rs b/src/cli/commands/auth/mod.rs index f5fb0ca..2b59f5c 100644 --- a/src/cli/commands/auth/mod.rs +++ b/src/cli/commands/auth/mod.rs @@ -1,31 +1,69 @@ -use clap::Subcommand; -use pesde::Project; +use crate::cli::config::read_config; +use clap::{Args, Subcommand}; +use pesde::{errors::ManifestReadError, Project}; mod login; mod logout; -mod set_token_override; mod whoami; +#[derive(Debug, Args)] +pub struct AuthSubcommand { + /// The index to use. Defaults to `default`, or the configured default index if current directory doesn't have a manifest + #[arg(short, long)] + pub index: Option, + + #[clap(subcommand)] + pub command: AuthCommands, +} + #[derive(Debug, Subcommand)] pub enum AuthCommands { - /// Logs in into GitHub, and stores the token + /// Sets a token for an index. Optionally gets it from GitHub Login(login::LoginCommand), /// Removes the stored token Logout(logout::LogoutCommand), /// Prints the username of the currently logged-in user #[clap(name = "whoami")] WhoAmI(whoami::WhoAmICommand), - /// Sets a token override for a specific repository - SetTokenOverride(set_token_override::SetTokenOverrideCommand), } -impl AuthCommands { +impl AuthSubcommand { pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> { - match self { - AuthCommands::Login(login) => login.run(project, reqwest), - AuthCommands::Logout(logout) => logout.run(), - AuthCommands::WhoAmI(whoami) => whoami.run(reqwest), - AuthCommands::SetTokenOverride(set_token_override) => set_token_override.run(), + let manifest = match project.deser_manifest() { + Ok(manifest) => Some(manifest), + Err(e) => match e { + ManifestReadError::Io(e) if e.kind() == std::io::ErrorKind::NotFound => None, + e => return Err(e.into()), + }, + }; + + let index_url = match self.index.as_deref() { + Some(index) => match index.try_into() { + Ok(url) => Some(url), + Err(_) => None, + }, + None => match manifest { + Some(_) => None, + None => Some(read_config()?.default_index), + }, + }; + + let index_url = match index_url { + Some(url) => url, + None => { + let index_name = self.index.as_deref().unwrap_or("default"); + + match manifest.unwrap().indices.get(index_name) { + Some(index) => index.clone(), + None => anyhow::bail!("index {index_name} not found"), + } + } + }; + + match self.command { + AuthCommands::Login(login) => login.run(index_url, project, reqwest), + AuthCommands::Logout(logout) => logout.run(index_url), + AuthCommands::WhoAmI(whoami) => whoami.run(index_url, reqwest), } } } diff --git a/src/cli/commands/auth/set_token_override.rs b/src/cli/commands/auth/set_token_override.rs deleted file mode 100644 index b2de68e..0000000 --- a/src/cli/commands/auth/set_token_override.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::cli::config::{read_config, write_config}; -use clap::Args; - -#[derive(Debug, Args)] -pub struct SetTokenOverrideCommand { - /// The repository to add the token to - #[arg(index = 1, value_parser = crate::cli::parse_gix_url)] - repository: gix::Url, - - /// The token to set - #[arg(index = 2)] - token: Option, -} - -impl SetTokenOverrideCommand { - pub fn run(self) -> anyhow::Result<()> { - let mut config = read_config()?; - - if let Some(token) = self.token { - println!("set token for {}", self.repository); - config.token_overrides.insert(self.repository, token); - } else { - println!("removed token for {}", self.repository); - config.token_overrides.remove(&self.repository); - } - - write_config(&config)?; - - Ok(()) - } -} diff --git a/src/cli/commands/auth/whoami.rs b/src/cli/commands/auth/whoami.rs index 06168b0..b89104a 100644 --- a/src/cli/commands/auth/whoami.rs +++ b/src/cli/commands/auth/whoami.rs @@ -1,4 +1,4 @@ -use crate::cli::{auth::get_token_login, get_token}; +use crate::cli::auth::{get_token_login, get_tokens}; use clap::Args; use colored::Colorize; @@ -6,16 +6,24 @@ use colored::Colorize; pub struct WhoAmICommand {} impl WhoAmICommand { - pub fn run(self, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> { - let token = match get_token()? { + pub fn run( + self, + index_url: gix::Url, + reqwest: reqwest::blocking::Client, + ) -> anyhow::Result<()> { + let tokens = get_tokens()?; + let token = match tokens.0.get(&index_url) { Some(token) => token, None => { - println!("not logged in"); + println!("not logged in into {index_url}"); return Ok(()); } }; - println!("logged in as {}", get_token_login(&reqwest, &token)?.bold()); + println!( + "logged in as {} into {index_url}", + get_token_login(&reqwest, token)?.bold() + ); Ok(()) } diff --git a/src/cli/commands/mod.rs b/src/cli/commands/mod.rs index a2daea0..e650ace 100644 --- a/src/cli/commands/mod.rs +++ b/src/cli/commands/mod.rs @@ -23,8 +23,7 @@ mod update; #[derive(Debug, clap::Subcommand)] pub enum Subcommand { /// Authentication-related commands - #[command(subcommand)] - Auth(auth::AuthCommands), + Auth(auth::AuthSubcommand), /// Configuration-related commands #[command(subcommand)] diff --git a/src/cli/commands/publish.rs b/src/cli/commands/publish.rs index 7fa643a..9151f0b 100644 --- a/src/cli/commands/publish.rs +++ b/src/cli/commands/publish.rs @@ -1,7 +1,7 @@ use anyhow::Context; use clap::Args; use colored::Colorize; -use reqwest::StatusCode; +use reqwest::{header::AUTHORIZATION, StatusCode}; use semver::VersionReq; use std::{ io::{Seek, Write}, @@ -457,13 +457,11 @@ impl PublishCommand { .finish() .context("failed to get archive bytes")?; - let source = PesdePackageSource::new( - manifest - .indices - .get(DEFAULT_INDEX_NAME) - .context("missing default index")? - .clone(), - ); + let index_url = manifest + .indices + .get(DEFAULT_INDEX_NAME) + .context("missing default index")?; + let source = PesdePackageSource::new(index_url.clone()); source .refresh(project) .context("failed to refresh source")?; @@ -501,14 +499,19 @@ impl PublishCommand { return Ok(()); } - let response = reqwest + let mut request = reqwest .post(format!("{}/v0/packages", config.api())) .multipart(reqwest::blocking::multipart::Form::new().part( "tarball", reqwest::blocking::multipart::Part::bytes(archive).file_name("package.tar.gz"), - )) - .send() - .context("failed to send request")?; + )); + + if let Some(token) = project.auth_config().tokens().get(index_url) { + log::debug!("using token for {index_url}"); + request = request.header(AUTHORIZATION, token); + } + + let response = request.send().context("failed to send request")?; let status = response.status(); let text = response.text().context("failed to get response text")?; diff --git a/src/cli/config.rs b/src/cli/config.rs index 675b306..9e49c68 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -1,10 +1,7 @@ -use std::collections::BTreeMap; - +use crate::cli::{auth::Tokens, home_dir}; use anyhow::Context; use serde::{Deserialize, Serialize}; -use crate::cli::home_dir; - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CliConfig { #[serde( @@ -18,15 +15,7 @@ pub struct CliConfig { )] pub scripts_repo: gix::Url, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub token: Option, - #[serde( - default, - skip_serializing_if = "BTreeMap::is_empty", - serialize_with = "crate::cli::serialize_string_url_map", - deserialize_with = "crate::cli::deserialize_string_url_map" - )] - pub token_overrides: BTreeMap, + pub tokens: Tokens, #[serde(default, skip_serializing_if = "Option::is_none")] pub last_checked_updates: Option<(chrono::DateTime, semver::Version)>, @@ -42,8 +31,7 @@ impl Default for CliConfig { .try_into() .unwrap(), - token: None, - token_overrides: Default::default(), + tokens: Tokens(Default::default()), last_checked_updates: None, } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 749e85a..35caa50 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,6 +1,4 @@ -use crate::cli::auth::get_token; use anyhow::Context; -use gix::bstr::BStr; use indicatif::MultiProgress; use pesde::{ lockfile::{DependencyGraph, DownloadedGraph, Lockfile}, @@ -10,7 +8,6 @@ use pesde::{ Project, }; use relative_path::RelativePathBuf; -use serde::{ser::SerializeMap, Deserialize, Deserializer, Serializer}; use std::{ collections::{BTreeMap, HashSet}, fs::create_dir_all, @@ -188,30 +185,6 @@ pub fn parse_gix_url(s: &str) -> Result { s.try_into() } -pub fn serialize_string_url_map( - url: &BTreeMap, - serializer: S, -) -> Result { - let mut map = serializer.serialize_map(Some(url.len()))?; - for (k, v) in url { - map.serialize_entry(&k.to_bstring().to_string(), v)?; - } - map.end() -} - -pub fn deserialize_string_url_map<'de, D: Deserializer<'de>>( - deserializer: D, -) -> Result, D::Error> { - BTreeMap::::deserialize(deserializer)? - .into_iter() - .map(|(k, v)| { - gix::Url::from_bytes(BStr::new(&k)) - .map(|k| (k, v)) - .map_err(serde::de::Error::custom) - }) - .collect() -} - #[allow(clippy::too_many_arguments)] pub fn download_graph( project: &Project, diff --git a/src/lib.rs b/src/lib.rs index e3022dc..bcf9c35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,8 +44,7 @@ pub(crate) const LINK_LIB_NO_FILE_FOUND: &str = "____pesde_no_export_file_found" /// Struct containing the authentication configuration #[derive(Debug, Default, Clone)] pub struct AuthConfig { - default_token: Option, - token_overrides: HashMap, + tokens: HashMap, git_credentials: Option, } @@ -55,18 +54,12 @@ impl AuthConfig { AuthConfig::default() } - /// Sets the default token - pub fn with_default_token>(mut self, token: Option) -> Self { - self.default_token = token.map(|s| s.as_ref().to_string()); - self - } - - /// Set the token overrides - pub fn with_token_overrides, S: AsRef>( + /// Set the tokens + pub fn with_tokens, S: AsRef>( mut self, tokens: I, ) -> Self { - self.token_overrides = tokens + self.tokens = tokens .into_iter() .map(|(url, s)| (url, s.as_ref().to_string())) .collect(); @@ -79,27 +72,15 @@ impl AuthConfig { self } - /// Get the default token - pub fn default_token(&self) -> Option<&str> { - self.default_token.as_deref() - } - - /// Get the token overrides - pub fn token_overrides(&self) -> &HashMap { - &self.token_overrides + /// Get the tokens + pub fn tokens(&self) -> &HashMap { + &self.tokens } /// Get the git credentials pub fn git_credentials(&self) -> Option<&Account> { self.git_credentials.as_ref() } - - pub(crate) fn get_token(&self, url: &gix::Url) -> Option<&str> { - self.token_overrides - .get(url) - .map(|s| s.as_str()) - .or(self.default_token.as_deref()) - } } /// The main struct of the pesde library, representing a project diff --git a/src/main.rs b/src/main.rs index 825a2fe..bc93952 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,9 +14,7 @@ use std::{ use crate::cli::version::{ check_for_updates, current_version, get_or_download_version, max_installed_version, }; -use crate::cli::{ - auth::get_token, config::read_config, home_dir, repos::update_repo_dependencies, HOME_DIR, -}; +use crate::cli::{auth::get_tokens, home_dir, repos::update_repo_dependencies, HOME_DIR}; mod cli; pub mod util; @@ -187,14 +185,14 @@ fn run() -> anyhow::Result<()> { let data_dir = home_dir()?.join("data"); create_dir_all(&data_dir).expect("failed to create data directory"); - let token = get_token()?; - let home_cas_dir = data_dir.join("cas"); create_dir_all(&home_cas_dir).expect("failed to create cas directory"); let project_root = get_root(&project_root_dir); let cas_dir = if get_root(&home_cas_dir) == project_root { + log::debug!("using home cas dir"); home_cas_dir } else { + log::debug!("using cas dir in {}", project_root.display()); project_root.join(HOME_DIR).join("cas") }; @@ -203,19 +201,11 @@ fn run() -> anyhow::Result<()> { project_workspace_dir, data_dir, cas_dir, - AuthConfig::new() - .with_default_token(token.clone()) - .with_token_overrides(read_config()?.token_overrides), + AuthConfig::new().with_tokens(get_tokens()?.0), ); let reqwest = { let mut headers = reqwest::header::HeaderMap::new(); - if let Some(token) = token { - headers.insert( - reqwest::header::AUTHORIZATION, - token.parse().context("failed to create auth header")?, - ); - } headers.insert( reqwest::header::ACCEPT, diff --git a/src/source/pesde/mod.rs b/src/source/pesde/mod.rs index c281dbb..bd2c693 100644 --- a/src/source/pesde/mod.rs +++ b/src/source/pesde/mod.rs @@ -7,7 +7,7 @@ use std::{ use gix::Url; use relative_path::RelativePathBuf; -use reqwest::header::{HeaderMap, ACCEPT, AUTHORIZATION}; +use reqwest::header::{ACCEPT, AUTHORIZATION}; use serde::{Deserialize, Serialize}; use pkg_ref::PesdePackageRef; @@ -273,29 +273,14 @@ impl PackageSource for PesdePackageSource { .replace("{PACKAGE_VERSION}", &pkg_ref.version.to_string()) .replace("{PACKAGE_TARGET}", &pkg_ref.target.to_string()); - let mut headers = HeaderMap::new(); - headers.insert( - ACCEPT, - "application/octet-stream" - .parse() - .map_err(|e| errors::DownloadError::InvalidHeaderValue("Accept".to_string(), e))?, - ); + let mut request = reqwest.get(&url).header(ACCEPT, "application/octet-stream"); - if let Some(token) = project.auth_config.get_token(&self.repo_url) { - log::debug!("using token for pesde package download"); - headers.insert( - AUTHORIZATION, - token.parse().map_err(|e| { - errors::DownloadError::InvalidHeaderValue("Authorization".to_string(), e) - })?, - ); + if let Some(token) = project.auth_config.tokens().get(&self.repo_url) { + log::debug!("using token for {}", self.repo_url); + request = request.header(AUTHORIZATION, token); } - let response = reqwest - .get(url) - .headers(headers) - .send()? - .error_for_status()?; + let response = request.send()?.error_for_status()?; let bytes = response.bytes()?; let mut decoder = flate2::read::GzDecoder::new(bytes.as_ref()); @@ -583,9 +568,5 @@ pub mod errors { /// Error writing index file #[error("error reading index file")] ReadIndex(#[source] std::io::Error), - - /// A header value was invalid - #[error("invalid header {0} value")] - InvalidHeaderValue(String, #[source] reqwest::header::InvalidHeaderValue), } } diff --git a/src/source/wally/mod.rs b/src/source/wally/mod.rs index f137d1e..d310d00 100644 --- a/src/source/wally/mod.rs +++ b/src/source/wally/mod.rs @@ -5,7 +5,7 @@ use std::{ use gix::Url; use relative_path::RelativePathBuf; -use reqwest::header::{HeaderMap, AUTHORIZATION}; +use reqwest::header::AUTHORIZATION; use serde::Deserialize; use tempfile::tempdir; @@ -178,33 +178,19 @@ impl PackageSource for WallyPackageSource { pkg_ref.version ); - let mut headers = HeaderMap::new(); - headers.insert( + let mut request = reqwest.get(&url).header( "Wally-Version", std::env::var("PESDE_WALLY_VERSION") .as_deref() - .unwrap_or("0.3.2") - .parse() - .map_err(|e| { - errors::DownloadError::InvalidHeaderValue("Wally-Version".to_string(), e) - })?, + .unwrap_or("0.3.2"), ); - if let Some(token) = project.auth_config.get_token(&self.repo_url) { - log::debug!("using token for wally package download"); - headers.insert( - AUTHORIZATION, - token.parse().map_err(|e| { - errors::DownloadError::InvalidHeaderValue("Authorization".to_string(), e) - })?, - ); + if let Some(token) = project.auth_config.tokens().get(&self.repo_url) { + log::debug!("using token for {}", self.repo_url); + request = request.header(AUTHORIZATION, token); } - let response = reqwest - .get(url) - .headers(headers) - .send()? - .error_for_status()?; + let response = request.send()?.error_for_status()?; let bytes = response.bytes()?; let mut archive = zip::ZipArchive::new(std::io::Cursor::new(bytes))?; @@ -355,9 +341,5 @@ pub mod errors { /// Error writing index file #[error("error writing index file")] WriteIndex(#[source] std::io::Error), - - /// A header value was invalid - #[error("invalid header {0} value")] - InvalidHeaderValue(String, #[source] reqwest::header::InvalidHeaderValue), } }