This fills in most of the major gaps that existed in the test cases, and implements small fixes where necessary. There are still some more niche cases to test, but this takes care of most of the common behaviour.
H2SE4OTIMSJDIPOE6KRLDLTT2IAACTNYX2NTUGRCWBY4NYQ5C7MQC
NE6CQKFXE4EHODG5M3P4OK5O2ICHPAXCNWSRIHB35BY5Q6RYXSGAC
L75UNYXJBN5AG4DFRUJZWL7ZXSALDRPWSR27FZUQY5EH5EWE5AFAC
4HALNYMMAQZ6HDUWNWX7RUK5RC7RURQUJDVS3J5PO73ECL3BW74QC
OEHY7S5TUVZSP2UKMAVVAAV2UF5EAJHQGJI4MK6V2G5ZIIBQFNQAC
CDJJGV754IVFINFUUX2YYWZYXPPKBVJYSE5VDJDD4FIHWTHWQ3WAC
SYEXNXCCG446XSFYGUHOPPWEV4YIGXZ5ZTLXM3G6CDCRMTPXGLYAC
3YJWXVMK5Y5JHH652RQLPBXO5VHYRNCFPGJI327K4WFXZNBPM6NQC
YYNQ2OCD5YBF2PM52RPR2W5YUMFGYYW5NBKVPQRZPD7UG4R43GYAC
WDGN6ZDCAOWWCMRLDEUP2563ZPBJABG5OEUXAKQLVXUR2UZKXAXQC
CQNKO3J26QGFNGW47XUGRRMUVZSG7PZ26MARM3TVZZG6DFDCUAIQC
E4AU7G76DH7DBWVG4WPKSIIB3LQXO2EDAPG46DZVMFB5SVYNCJJAC
N6D2GO25JWBGZKIOEXIXA45YTORG33KG7VVN2HUFQEWT7BIYFX4AC
PDMUOY2MJTWL7SOXWHSXUSTNWMOGZSK7E6PEZVZPF7QBJRAUWJJAC
UVEP3FDB4TS7JB7RQCUZGTW652H4EV2FC5YPWIJWARRSE44CQ7EQC
574FXSXKJF6OVHKHGIFKRFNJOR6URJ23JSQQ47YGMN7XGRXDCSGAC
YQLVP7CHQKIQCKQFRNI5RHSEBHLXXMRQI662MEXXBE5S63H5XC7AC
let identity = Identity::new(id_name("new_minimal"), None, None, None, None)?;
let identity = Identity::new(id_name("new_minimal"), None, None, None, None, None, None)?;
identity.run(SubCommand::New)?;
Ok(())
}
#[test]
fn new_full() -> Result<(), Error> {
let identity = Identity::new(
id_name("new_full"),
Some(default::FULL_NAME.to_string()),
Some(Interaction::new(
prompt::EMAIL,
InteractionType::Input(default::EMAIL.to_string()),
)),
Some(Interaction::new(
prompt::EXPIRY_DATE,
InteractionType::Input(default::EXPIRY.to_string()),
)),
Some(Interaction::new(
prompt::LOGIN,
InteractionType::Input(default::LOGIN.to_string()),
)),
Some(Interaction::new(
prompt::ORIGIN,
InteractionType::Input(default::ORIGIN.to_string()),
)),
Some(Interaction::new(
prompt::PASSWORD,
InteractionType::Password {
input: default::PASSWORD.to_string(),
confirm: Some(prompt::PASSWORD_REPROMPT.to_string()),
},
)),
)?;
prompt::ENCRYPTION_CONFIRM,
prompt::EXPIRY_DATE,
InteractionType::Input(default::EXPIRY.to_string()),
)),
None,
None,
None,
)?;
identity.run(SubCommand::New)?;
Ok(())
}
#[test]
fn new_login() -> Result<(), Error> {
let identity = Identity::new(
id_name("new_login"),
None,
None,
None,
Some(Interaction::new(
prompt::LOGIN,
InteractionType::Input(default::LOGIN.to_string()),
)),
None,
None,
)?;
identity.run(SubCommand::New)?;
Ok(())
}
#[test]
fn new_origin() -> Result<(), Error> {
let identity = Identity::new(
id_name("new_origin"),
None,
None,
None,
None,
Some(Interaction::new(
prompt::ORIGIN,
InteractionType::Input(default::ORIGIN.to_string()),
)),
None,
)?;
identity.run(SubCommand::New)?;
Ok(())
}
#[test]
fn new_password() -> Result<(), Error> {
let identity = Identity::new(
id_name("new_password"),
None,
None,
None,
None,
None,
Some(Interaction::new(
prompt::PASSWORD,
pub const DEFAULT_FULL_NAME: &str = "Firstname Lastname";
pub const DEFAULT_EMAIL: &str = "person@example.com";
pub const DEFAULT_EXPIRY: &str = "2056-01-01";
pub const DEFAULT_LOGIN: &str = "my_username";
pub const DEFAULT_ORIGIN: &str = "ssh.pijul.com";
pub const DEFAULT_PASSWORD: &str = "correct-horse-battery-staple";
pub mod default {
pub const FULL_NAME: &str = "Firstname Lastname";
pub const EMAIL: &str = "person@example.com";
pub const EXPIRY: &str = "2056-01-01";
pub const LOGIN: &str = "my_username";
pub const ORIGIN: &str = "ssh.pijul.com";
pub const PASSWORD: &str = "correct-horse-battery-staple";
}
pub const REMOTE_CONFIRM: &str = "Do you want to link this identity to a remote?";
pub const REMOTE_USERNAME: &str = "Remote username";
pub const REMOTE_ADDRESS: &str = "Remote URL";
pub const ENCRYPTION_CONFIRM: &str = "Do you want to change the encryption?";
pub const LOGIN: &str = "Remote username";
pub const ORIGIN: &str = "Remote URL";
pub const PASSWORD_CONFIRM: &str = "Secret key password";
pub const PASSWORD_REPROMPT: &str = "Confirm password";
pub mod confirm {
pub const EXPIRY: &str = "Do you want this key to expire?";
pub const REMOTE: &str = "Do you want to link this identity to a remote?";
pub const ENCRYPTION: &str = "Do you want to change the encryption?";
}
// TODO: handle more complex fields
println!(
"Login: {:?} (should be {:?})",
toml_data.get("login"),
self.login
.clone()
.and_then(|x| Some(x.valid_input().as_string()))
);
if let Some(login) = &self.login {
assert_eq!(
login.valid_input().as_string().as_str(),
toml_data.get("login").unwrap().as_str().unwrap()
);
} else {
let default = toml::value::Value::String(String::new());
let data = toml_data.get("login").unwrap_or(&default).as_str().unwrap();
assert!(data.is_empty() || data == whoami::username());
}
if let Some(origin) = &self.origin {
assert_eq!(
origin.valid_input().as_string().as_str(),
toml_data.get("origin").unwrap().as_str().unwrap()
);
} else {
let default = toml::value::Value::String(String::new());
let data = toml_data
.get("origin")
.unwrap_or(&default)
.as_str()
.unwrap();
assert!(data.is_empty() || data == "ssh.pijul.com");
}
if let Some(expiry) = &self.expiry {
let time_stamp = toml_data
.get("public_key")
.unwrap()
.get("expires")
.unwrap()
.as_str()
.unwrap();
let timestamp = dateparser::parse_with_timezone(&time_stamp, &chrono::offset::Utc)?;
assert_eq!(
expiry.valid_input().as_string(),
timestamp.format("%Y-%m-%d").to_string()
);
} else {
assert!(toml_data
.get("public_key")
.unwrap()
.get("expires")
.is_none());
}
let mut secret_key_file = std::fs::File::open(
self.config_path
.join("identities")
.join(self.id_name.valid_input().as_string())
.join("secret_key.json"),
)?;
let mut secret_key_text = String::new();
secret_key_file.read_to_string(&mut secret_key_text)?;
let secret_key: libpijul::key::SecretKey = serde_json::from_str(&secret_key_text)?;
assert_eq!(secret_key.encryption.is_some(), self.password.is_some());
pijul_cmd.arg("--name").arg(expiry.get_input(should_fail));
pijul_cmd.arg("--expiry").arg(expiry.get_input(should_fail));
}
if let Some(login) = self.login.clone() {
pijul_cmd.arg("--login").arg(login.get_input(should_fail));
}
if let Some(origin) = self.origin.clone() {
pijul_cmd.arg("--origin").arg(origin.get_input(should_fail));
}
if self.password.is_some() {
pijul_cmd.arg("--read-password");
Interaction::new(prompt::REMOTE_CONFIRM, InteractionType::Confirm(false))
.interact(&mut session)?;
let remote_data = self.login.is_some() || self.origin.is_some();
Interaction::new(
prompt::confirm::REMOTE,
InteractionType::Confirm(remote_data),
)
.interact(&mut session)?;
if remote_data {
if let Some(login) = self.login.clone() {
login.interact(&mut session)?;
} else {
// Use an empty login
Interaction::new(prompt::LOGIN, InteractionType::Input(String::new()))
.interact(&mut session)?;
}
if let Some(origin) = self.origin.clone() {
origin.interact(&mut session)?;
} else {
// Use an empty origin
Interaction::new(prompt::ORIGIN, InteractionType::Input(String::new()))
.interact(&mut session)?;
}
}
pub async fn create(self, no_prompt: bool) -> Result<Self, IdentityCreateError> {
/// * `link_remote` - Override if the identity should be exchanged with the remote.
pub async fn create(
self,
no_prompt: bool,
link_remote: bool,
) -> Result<Self, IdentityCreateError> {
// Make a new, identity based upon the info given
let public_key = if let Some(date) = expiry {
let mut pk = default.identity.public_key.clone();
pk.expires = Some(date);
pk
let pw = if password {
Some(
dialoguer::Password::with_theme(config::load_theme()?.as_ref())
.with_prompt("Secret key password")
.with_confirmation("Confirm password", "Password mismatch")
.interact()?,
)