Skip to content

Commit 1ccdc8b

Browse files
committed
Update a credentials file format. Implement tests for login command.
1 parent aa17b61 commit 1ccdc8b

File tree

2 files changed

+182
-35
lines changed

2 files changed

+182
-35
lines changed

src/cargo/util/config.rs

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::collections::HashSet;
55
use std::env;
66
use std::fmt;
77
use std::fs::{self, File};
8+
use std::io::SeekFrom;
89
use std::io::prelude::*;
910
use std::mem;
1011
use std::path::{Path, PathBuf};
@@ -429,28 +430,50 @@ impl Config {
429430
Ok(())
430431
}).chain_err(|| "Couldn't load Cargo configuration")?;
431432

432-
let mut map = match cfg {
433-
CV::Table(map, _) => map,
433+
self.load_credentials(&mut cfg)?;
434+
match cfg {
435+
CV::Table(map, _) => Ok(map),
434436
_ => unreachable!(),
435-
};
437+
}
438+
}
436439

440+
fn load_credentials(&self, cfg: &mut ConfigValue) -> CargoResult<()> {
437441
let home_path = self.home_path.clone().into_path_unlocked();
438-
let token = load_credentials(&home_path)?;
439-
if let Some(t) = token {
440-
if !t.is_empty() {
441-
let mut registry = map.entry("registry".into())
442-
.or_insert(CV::Table(HashMap::new(), PathBuf::from(".")));
443-
match *registry {
444-
CV::Table(ref mut m, _) => {
445-
m.insert("token".into(),
446-
CV::String(t, home_path.join("credentials")));
447-
}
448-
_ => unreachable!(),
449-
}
450-
}
442+
let credentials = home_path.join("credentials");
443+
if !fs::metadata(&credentials).is_ok() {
444+
return Ok(());
451445
}
452446

453-
Ok(map)
447+
let mut contents = String::new();
448+
let mut file = File::open(&credentials)?;
449+
file.read_to_string(&mut contents).chain_err(|| {
450+
format!("failed to read configuration file `{}`", credentials.display())
451+
})?;
452+
453+
let toml = cargo_toml::parse(&contents,
454+
&credentials,
455+
self).chain_err(|| {
456+
format!("could not parse TOML configuration in `{}`", credentials.display())
457+
})?;
458+
459+
let value = CV::from_toml(&credentials, toml).chain_err(|| {
460+
format!("failed to load TOML configuration from `{}`", credentials.display())
461+
})?;
462+
463+
let mut cfg = match *cfg {
464+
CV::Table(ref mut map, _) => map,
465+
_ => unreachable!(),
466+
};
467+
468+
let mut registry = cfg.entry("registry".into())
469+
.or_insert(CV::Table(HashMap::new(),
470+
PathBuf::from(".")));
471+
registry.merge(value).chain_err(|| {
472+
format!("failed to merge configuration at `{}`",
473+
credentials.display())
474+
})?;
475+
476+
Ok(())
454477
}
455478

456479
/// Look for a path for `tool` in an environment variable or config path, but return `None`
@@ -567,6 +590,21 @@ impl ConfigValue {
567590
}
568591
}
569592

593+
fn into_toml(self) -> toml::Value {
594+
match self {
595+
CV::Boolean(s, _) => toml::Value::Boolean(s),
596+
CV::String(s, _) => toml::Value::String(s),
597+
CV::Integer(i, _) => toml::Value::Integer(i),
598+
CV::List(l, _) => toml::Value::Array(l
599+
.into_iter()
600+
.map(|(s, _)| toml::Value::String(s))
601+
.collect()),
602+
CV::Table(l, _) => toml::Value::Table(l.into_iter()
603+
.map(|(k, v)| (k, v.into_toml()))
604+
.collect()),
605+
}
606+
}
607+
570608
fn merge(&mut self, from: ConfigValue) -> CargoResult<()> {
571609
match (self, from) {
572610
(&mut CV::String(..), CV::String(..)) |
@@ -774,8 +812,21 @@ pub fn save_credentials(cfg: &Config,
774812
"credentials' config file")?
775813
};
776814

777-
file.write_all(token.as_bytes())?;
778-
file.file().set_len(token.len() as u64)?;
815+
let mut contents = String::new();
816+
file.read_to_string(&mut contents).chain_err(|| {
817+
format!("failed to read configuration file `{}`",
818+
file.path().display())
819+
})?;
820+
let mut toml = cargo_toml::parse(&contents, file.path(), cfg)?;
821+
toml.as_table_mut()
822+
.unwrap()
823+
.insert("token".to_string(),
824+
ConfigValue::String(token, file.path().to_path_buf()).into_toml());
825+
826+
let contents = toml.to_string();
827+
file.seek(SeekFrom::Start(0))?;
828+
file.write_all(contents.as_bytes())?;
829+
file.file().set_len(contents.len() as u64)?;
779830
set_permissions(file.file(), 0o600)?;
780831

781832
return Ok(());
@@ -796,19 +847,3 @@ pub fn save_credentials(cfg: &Config,
796847
Ok(())
797848
}
798849
}
799-
800-
fn load_credentials(home: &PathBuf) -> CargoResult<Option<String>> {
801-
let credentials = home.join("credentials");
802-
if !fs::metadata(&credentials).is_ok() {
803-
return Ok(None);
804-
}
805-
806-
let mut token = String::new();
807-
let mut file = File::open(&credentials)?;
808-
file.read_to_string(&mut token).chain_err(|| {
809-
format!("failed to read configuration file `{}`",
810-
credentials.display())
811-
})?;
812-
813-
Ok(Some(token.trim().into()))
814-
}

tests/login.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#[macro_use]
2+
extern crate cargotest;
3+
extern crate cargo;
4+
extern crate hamcrest;
5+
extern crate toml;
6+
7+
use std::io::prelude::*;
8+
use std::fs::{self, File};
9+
10+
use cargotest::cargo_process;
11+
use cargotest::support::execs;
12+
use cargotest::support::registry::registry;
13+
use cargotest::install::cargo_home;
14+
use hamcrest::{assert_that, existing_file, is_not};
15+
16+
const TOKEN: &str = "test-token";
17+
const CONFIG_FILE: &str = r#"
18+
[registry]
19+
token = "api-token"
20+
"#;
21+
22+
fn setup_old_credentials() {
23+
let config = cargo_home().join("config");
24+
t!(fs::create_dir_all(config.parent().unwrap()));
25+
t!(t!(File::create(&config)).write_all(&CONFIG_FILE.as_bytes()));
26+
}
27+
28+
fn setup_new_credentials() {
29+
let config = cargo_home().join("credentials");
30+
t!(fs::create_dir_all(config.parent().unwrap()));
31+
t!(t!(File::create(&config)).write_all(br#"
32+
token = "api-token"
33+
"#));
34+
}
35+
36+
fn check_host_token(toml: toml::Value) -> bool {
37+
match toml {
38+
toml::Value::Table(table) => match table.get("token") {
39+
Some(v) => match v {
40+
&toml::Value::String(ref token) => (token.as_str() == TOKEN),
41+
_ => false,
42+
},
43+
None => false,
44+
},
45+
_ => false,
46+
}
47+
}
48+
49+
#[test]
50+
fn login_with_old_credentials() {
51+
setup_old_credentials();
52+
53+
assert_that(cargo_process().arg("login")
54+
.arg("--host").arg(registry().to_string()).arg(TOKEN),
55+
execs().with_status(0));
56+
57+
let config = cargo_home().join("config");
58+
assert_that(&config, existing_file());
59+
60+
let mut contents = String::new();
61+
File::open(&config).unwrap().read_to_string(&mut contents).unwrap();
62+
assert!(CONFIG_FILE == &contents);
63+
64+
let credentials = cargo_home().join("credentials");
65+
assert_that(&credentials, existing_file());
66+
67+
contents.clear();
68+
File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap();
69+
assert!(check_host_token(contents.parse().unwrap()));
70+
}
71+
72+
#[test]
73+
fn login_with_new_credentials() {
74+
setup_new_credentials();
75+
76+
assert_that(cargo_process().arg("login")
77+
.arg("--host").arg(registry().to_string()).arg(TOKEN),
78+
execs().with_status(0));
79+
80+
let config = cargo_home().join("config");
81+
assert_that(&config, is_not(existing_file()));
82+
83+
let credentials = cargo_home().join("credentials");
84+
assert_that(&credentials, existing_file());
85+
86+
let mut contents = String::new();
87+
File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap();
88+
assert!(check_host_token(contents.parse().unwrap()));
89+
}
90+
91+
#[test]
92+
fn login_with_old_and_new_credentials() {
93+
setup_new_credentials();
94+
login_with_old_credentials();
95+
}
96+
97+
#[test]
98+
fn login_without_credentials() {
99+
assert_that(cargo_process().arg("login")
100+
.arg("--host").arg(registry().to_string()).arg(TOKEN),
101+
execs().with_status(0));
102+
103+
let config = cargo_home().join("config");
104+
assert_that(&config, is_not(existing_file()));
105+
106+
let credentials = cargo_home().join("credentials");
107+
assert_that(&credentials, existing_file());
108+
109+
let mut contents = String::new();
110+
File::open(&credentials).unwrap().read_to_string(&mut contents).unwrap();
111+
assert!(check_host_token(contents.parse().unwrap()));
112+
}

0 commit comments

Comments
 (0)