diff --git a/src/bin/login.rs b/src/bin/login.rs index a9d6b1a710a..468c8453c79 100644 --- a/src/bin/login.rs +++ b/src/bin/login.rs @@ -40,26 +40,29 @@ pub fn execute(options: Options, config: &Config) -> CliResult { &options.flag_color, options.flag_frozen, options.flag_locked)?; - let token = match options.arg_token.clone() { + let token = match options.arg_token { Some(token) => token, None => { - let src = SourceId::crates_io(config)?; - let mut src = RegistrySource::remote(&src, config); - src.update()?; - let config = src.config()?.unwrap(); - let host = options.flag_host.clone().unwrap_or(config.api); + let host = match options.flag_host { + Some(ref host) => host.clone(), + None => { + let src = SourceId::crates_io(config)?; + let mut src = RegistrySource::remote(&src, config); + src.update()?; + src.config()?.unwrap().api + } + }; + println!("please visit {}me and paste the API Token below", host); let mut line = String::new(); let input = io::stdin(); input.lock().read_line(&mut line).chain_err(|| { "failed to read stdin" })?; - line + line.trim().to_string() } }; - let token = token.trim().to_string(); - ops::registry_login(config, token)?; + ops::registry_login(config, token, options.flag_host)?; Ok(()) } - diff --git a/src/cargo/core/source.rs b/src/cargo/core/source.rs index e7a3ce4a2e8..7487b8e9c9b 100644 --- a/src/cargo/core/source.rs +++ b/src/cargo/core/source.rs @@ -206,7 +206,7 @@ impl SourceId { /// This is the main cargo registry by default, but it can be overridden in /// a `.cargo/config`. pub fn crates_io(config: &Config) -> CargoResult { - let cfg = ops::registry_configuration(config)?; + let cfg = ops::registry_configuration(config, None)?; let url = if let Some(ref index) = cfg.index { static WARNED: AtomicBool = ATOMIC_BOOL_INIT; if !WARNED.swap(true, SeqCst) { diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index 35ec30257c2..cc63cb7530c 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -16,7 +16,7 @@ use core::dependency::Kind; use core::manifest::ManifestMetadata; use ops; use sources::{RegistrySource}; -use util::config::{self, Config}; +use util::config::{self, Config, Value, Definition}; use util::paths; use util::ToUrl; use util::errors::{CargoError, CargoResult, CargoResultExt}; @@ -176,10 +176,24 @@ fn transmit(config: &Config, } } -pub fn registry_configuration(config: &Config) -> CargoResult { - let index = config.get_string("registry.index")?.map(|p| p.val); - let token = config.get_string("registry.token")?.map(|p| p.val); - Ok(RegistryConfig { index: index, token: token }) +pub fn registry_configuration(config: &Config, + host: Option) -> CargoResult { + let (index, token) = match host { + Some(host) => { + (Some(Value { val: host.clone(), definition: Definition::Environment }), + config.get_string(&format!("registry.{}.token", host))?) + } + None => { + // Checking out for default index and token + (config.get_string("registry.index")?, + config.get_string("registry.token")?) + } + }; + + Ok(RegistryConfig { + index: index.map(|p| p.val), + token: token.map(|p| p.val) + }) } pub fn registry(config: &Config, @@ -189,7 +203,7 @@ pub fn registry(config: &Config, let RegistryConfig { token: token_config, index: _index_config, - } = registry_configuration(config)?; + } = registry_configuration(config, index.clone())?; let token = token.or(token_config); let sid = match index { Some(index) => SourceId::for_registry(&index.to_url()?), @@ -280,15 +294,21 @@ pub fn http_timeout(config: &Config) -> CargoResult> { Ok(env::var("HTTP_TIMEOUT").ok().and_then(|s| s.parse().ok())) } -pub fn registry_login(config: &Config, token: String) -> CargoResult<()> { - let RegistryConfig { index: _, token: old_token } = registry_configuration(config)?; +pub fn registry_login(config: &Config, + token: String, + host: Option) -> CargoResult<()> { + let RegistryConfig { + index: _, + token: old_token + } = registry_configuration(config, host.clone())?; + if let Some(old_token) = old_token { if old_token == token { return Ok(()); } } - config::save_credentials(config, token) + config::save_credentials(config, token, host) } pub struct OwnersOptions { diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 2df174740df..dde70544ec5 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -835,23 +835,36 @@ fn walk_tree(pwd: &Path, mut walk: F) -> CargoResult<()> } pub fn save_credentials(cfg: &Config, - token: String) -> CargoResult<()> { + token: String, + host: Option) -> CargoResult<()> { let mut file = { cfg.home_path.create_dir()?; cfg.home_path.open_rw(Path::new("credentials"), cfg, - "credentials' config file")? + "credentials' config file")? + }; + + let (key, value) = { + let key = "token".to_string(); + let value = ConfigValue::String(token, file.path().to_path_buf()); + + if let Some(host) = host { + let mut map = HashMap::new(); + map.insert(key, value); + (host, CV::Table(map, file.path().to_path_buf())) + } else { + (key, value) + } }; let mut contents = String::new(); file.read_to_string(&mut contents).chain_err(|| { - format!("failed to read configuration file `{}`", - file.path().display()) + format!("failed to read configuration file `{}`", file.path().display()) })?; + let mut toml = cargo_toml::parse(&contents, file.path(), cfg)?; toml.as_table_mut() .unwrap() - .insert("token".to_string(), - ConfigValue::String(token, file.path().to_path_buf()).into_toml()); + .insert(key, value.into_toml()); let contents = toml.to_string(); file.seek(SeekFrom::Start(0))?; diff --git a/tests/login.rs b/tests/login.rs index d3b9b601b2f..7907da15763 100644 --- a/tests/login.rs +++ b/tests/login.rs @@ -30,20 +30,35 @@ fn setup_old_credentials() { fn setup_new_credentials() { let config = cargo_home().join("credentials"); t!(fs::create_dir_all(config.parent().unwrap())); - t!(t!(File::create(&config)).write_all(br#" + t!(t!(File::create(&config)).write_all(format!(r#" token = "api-token" - "#)); + + ["{registry}"] + token = "api-token" + "#, registry = registry().to_string()) + .as_bytes())); } -fn check_host_token(toml: toml::Value) -> bool { +fn check_host_token(mut toml: toml::Value, host_key: &str) -> bool { + for &key in [host_key, "token"].into_iter() { + if key.is_empty() { + continue + } + + match toml { + toml::Value::Table(table) => { + if let Some(v) = table.get(key) { + toml = v.clone(); + } else { + return false; + } + } + _ => break, + } + } + match toml { - toml::Value::Table(table) => match table.get("token") { - Some(v) => match v { - &toml::Value::String(ref token) => (token.as_str() == TOKEN), - _ => false, - }, - None => false, - }, + toml::Value::String(token) => (&token == TOKEN), _ => false, } } @@ -68,7 +83,7 @@ fn login_with_old_credentials() { contents.clear(); File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap(); - assert!(check_host_token(contents.parse().unwrap())); + assert!(check_host_token(contents.parse().unwrap(), ®istry().to_string())); } #[test] @@ -87,7 +102,7 @@ fn login_with_new_credentials() { let mut contents = String::new(); File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap(); - assert!(check_host_token(contents.parse().unwrap())); + assert!(check_host_token(contents.parse().unwrap(), ®istry().to_string())); } #[test] @@ -101,6 +116,8 @@ fn login_without_credentials() { assert_that(cargo_process().arg("login") .arg("--host").arg(registry().to_string()).arg(TOKEN), execs().with_status(0)); + assert_that(cargo_process().arg("login").arg(TOKEN), + execs().with_status(0)); let config = cargo_home().join("config"); assert_that(&config, is_not(existing_file())); @@ -110,7 +127,9 @@ fn login_without_credentials() { let mut contents = String::new(); File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap(); - assert!(check_host_token(contents.parse().unwrap())); + let toml: toml::Value = contents.parse().unwrap(); + assert!(check_host_token(toml.clone(), ®istry().to_string())); + assert!(check_host_token(toml, "")); } #[test] @@ -118,11 +137,19 @@ fn new_credentials_is_used_instead_old() { setup_old_credentials(); setup_new_credentials(); + assert_that(cargo_process().arg("login").arg(TOKEN), + execs().with_status(0)); + assert_that(cargo_process().arg("login") .arg("--host").arg(registry().to_string()).arg(TOKEN), execs().with_status(0)); let config = Config::new(Shell::new(), cargo_home(), cargo_home()); + let token = config.get_string("registry.token").unwrap().map(|p| p.val); assert!(token.unwrap() == TOKEN); + + let token_host = config.get_string(&format!(r#"registry.{}.token"#, registry().to_string())) + .unwrap().map(|p| p.val); + assert!(token_host.unwrap() == TOKEN); } diff --git a/tests/publish.rs b/tests/publish.rs index 968281f6219..67ab05e0985 100644 --- a/tests/publish.rs +++ b/tests/publish.rs @@ -13,6 +13,7 @@ use std::path::PathBuf; use cargotest::support::git::repo; use cargotest::support::paths; use cargotest::support::{project, execs}; +use cargotest::install::cargo_home; use flate2::read::GzDecoder; use hamcrest::assert_that; use tar::Archive; @@ -24,14 +25,21 @@ fn upload_path() -> PathBuf { paths::root().join("upload") } fn upload() -> Url { Url::from_file_path(&*upload_path()).ok().unwrap() } fn setup() { - let config = paths::root().join(".cargo/config"); + let config = cargo_home().join("config"); t!(fs::create_dir_all(config.parent().unwrap())); t!(t!(File::create(&config)).write_all(br#" [registry] - token = "api-token" + token = "api-token" "#)); t!(fs::create_dir_all(&upload_path().join("api/v1/crates"))); + let credentials = cargo_home().join("credentials"); + t!(t!(File::create(&credentials)).write_all(format!(r#" + ["{registry}"] + token = "api-token" + "#, registry = registry().to_string()) + .as_bytes())); + repo(®istry_path()) .file("config.json", &format!(r#"{{ "dl": "{0}",