Update codebase to use new identity management
Dependencies
- [2]
MU6P2JXGSSH: return with an error if the host key is wrong, rather than denying authentication - [3]
C267PHOHTags: dropping useless Hashes in favour of Merkles - [4]
NAUECZW3Fixing the map between keys and identities - [5]
SXEYMYF7Fixing the bad changes in history (unfortunately, by rebooting). - [6]
E7UUQQCCApply changes with prefixes in .pijul/changes - [7]
6F6AAHK4Simplifying pijul::commands::log, and fixing Broken Pipe errors - [8]
FDEVV5NGTag check - [9]
SZWBLWZ4Reading ~/.ssh/config - [10]
3KRGVQFUDo not update the mtime of unmodified files - [11]
RUBBHYZ7Removing unnecessary async/await - [12]
TFPETWTVAdd config options for patch message templates - [13]
L4JXJHWXpijul/*: reorganize imports and remove extern crate - [14]
U6TQX5Z2pager function respects cli option and user config files, PAGER env var - [15]
F6V27C3MFixing the "old file optimisation" in record, after the move to parallelisable records - [16]
NS36CJCOValidating timestamps in record - [17]
EEBKW7VTKeys and identities - [18]
LJFJEX43Fixing newline issues in the protocol over OpenSSH - [19]
TEQS7C6CFixing filters in `pijul log` - [20]
IIV3EL2XCleanup, formatting, and fixing the Git feature - [21]
QL6K2ZM3Tags - [22]
RRCSHAYZFormatting - [23]
EUZFFJSOUpdating Pijul with the latest changes in Libpijul - [24]
OU6JOR3CAdd path filtering for log, add json output for log - [25]
OU243LABSupport for staging - [26]
HKA66XOQUpdating Thrussh version for long or massive connections - [27]
2RXOCWUWMaking libpijul deterministic (and getting rid of `rand`) - [28]
6U42MTEZFixing log -- filters, along with performance improvements - [29]
ZDK3GNDBTag transactions (including a massive refactoring of errors) - [30]
CCLLB7OIUpgrading to Sanakirja 0.15 + version bump - [31]
G6S6PWZEDo not touch the channel if this is a partial record - [32]
I24UEJQLVarious post-fire fixes - [33]
UDHP4ZVBFixing SSH asynchronicity issues - [34]
AAXP2534Tags: completing the subcommand - [35]
SLJ3OHD4unrecord: show list of changes if none were given as arguments - [36]
TPEH2XNB1.0.0-alpha.28, with Tokio 1.0 - [37]
I52XSRUHMassive cleanup, and simplification - [38]
LGEJSLTYFixing output (including its uses in reset and pull) - [39]
SMMBFECLConverting to the new patch format "online" - [40]
GUL4M5FICleanup and formatting - [41]
I6DVZEFUDo not ask for user input if the SSH channel is already closed - [42]
A3RM526YIntegrating identity malleability - [43]
MU5GSJAWPartial push and pull (WARNING: breaks the existing protocol)
Change contents
- replacement in pijul/src/commands/tag.rs at line 86
pub fn run(self) -> Result<(), anyhow::Error> {pub async fn run(self) -> Result<(), anyhow::Error> { - replacement in pijul/src/commands/tag.rs at line 126
let header = header(author.as_deref(), message, timestamp)?;let header = header(author.as_deref(), message, timestamp).await?; - replacement in pijul/src/commands/tag.rs at line 270
fn header(async fn header( - replacement in pijul/src/commands/tag.rs at line 280[5.44]→[5.757:1002](∅→∅),[5.13668]→[5.757:1002](∅→∅),[5.757]→[5.757:1002](∅→∅),[5.1002]→[5.13669:13820](∅→∅),[5.84]→[5.1034:1044](∅→∅),[5.13820]→[5.1034:1044](∅→∅),[5.1034]→[5.1034:1044](∅→∅)
} else if let Some(mut dir) = crate::config::global_config_dir() {dir.push("publickey.json");if let Ok(key) = std::fs::File::open(&dir) {let k: libpijul::key::PublicKey = serde_json::from_reader(key).unwrap();b.insert("key".to_string(), k.key);} else {bail!("No identity configured yet. Please use `pijul key` to create one")}} else if let Some(_dir) = crate::config::global_config_dir() {let k = crate::identity::public_key(&crate::identity::choose_identity_name(false).await?)?;b.insert("key".to_string(), k.key); - edit in pijul/src/commands/record.rs at line 50
/// Identity to sign changes with#[clap(long = "identity")]pub identity: Option<String>, - replacement in pijul/src/commands/record.rs at line 65
pub fn run(self) -> Result<(), anyhow::Error> {pub async fn run(self) -> Result<(), anyhow::Error> { - replacement in pijul/src/commands/record.rs at line 132
self.header()?self.header().await? - replacement in pijul/src/commands/record.rs at line 145
let (_, key) = super::load_key()?;let (_, key) = crate::identity::decrypt_secret_key(&crate::identity::choose_identity_name(false).await?,None,)?; - edit in pijul/src/commands/record.rs at line 176
path.push("publickey.json");std::fs::File::create(&path)?; - replacement in pijul/src/commands/record.rs at line 210
fn header(&self) -> Result<ChangeHeader, anyhow::Error> {async fn header(&self) -> Result<ChangeHeader, anyhow::Error> { - replacement in pijul/src/commands/record.rs at line 216[5.129]→[5.2177:2438](∅→∅),[5.14377]→[5.2177:2438](∅→∅),[5.2177]→[5.2177:2438](∅→∅),[5.2438]→[5.14378:14430](∅→∅),[5.14430]→[5.173:284](∅→∅),[5.173]→[5.173:284](∅→∅),[5.284]→[5.2474:2488](∅→∅),[5.2474]→[5.2474:2488](∅→∅)
} else if let Some(mut dir) = crate::config::global_config_dir() {dir.push("publickey.json");if let Ok(key) = std::fs::File::open(&dir) {let k: libpijul::key::PublicKey = serde_json::from_reader(key).unwrap();b.insert("key".to_string(), k.key);} else {bail!("No identity configured yet. Please use `pijul key` to create one")}} else {let public_key = crate::identity::public_key(&self.identity.clone().unwrap_or(crate::identity::choose_identity_name(false).await?),);b.insert("key".to_string(), public_key?.key); - edit in pijul/src/commands/record.rs at line 225
- replacement in pijul/src/commands/protocol.rs at line 12
use log::{debug, error};use log::{debug, error, warn}; - edit in pijul/src/commands/protocol.rs at line 374
fn get_public_key() -> Result<libpijul::key::PublicKey, anyhow::Error> {if let Some(mut dir) = crate::config::global_config_dir() {dir.push("publickey.json");if let Ok(mut pkf) = std::fs::File::open(&dir) {if let Ok(pkf) = serde_json::from_reader(&mut pkf) {return Ok(pkf);}}}bail!("No public key found")} - replacement in pijul/src/commands/protocol.rs at line 391
let public_key: libpijul::key::PublicKey = if let Ok(pk) = get_public_key() {pk} else {return Ok(());};if !done.insert(public_key.key.clone()) {return Ok(());}if let Ok((config, last_modified)) = crate::config::Global::load() {serde_json::to_writer(&mut o,&crate::Identity {public_key,email: config.author.email,name: config.author.full_name,login: config.author.name,origin: String::new(),last_modified,},).unwrap();writeln!(o)?;} else {debug!("no global config");}warn!("Skipping serializing old public key format.");return Ok(()); - replacement in pijul/src/commands/mod.rs at line 59
mod key;pub use key::*;mod identity;pub use identity::*; - replacement in pijul/src/commands/mod.rs at line 280
#[derive(Debug, Serialize, Deserialize)]#[derive(Clone, Debug, Serialize, Deserialize)] - edit in pijul/src/commands/mod.rs at line 282
pub public_key: libpijul::key::PublicKey, - edit in pijul/src/commands/mod.rs at line 291
pub public_key: libpijul::key::PublicKey, - edit in pijul/src/commands/mod.rs at line 294[5.20228]→[4.67:157](∅→∅),[4.157]→[5.20290:20787](∅→∅),[5.20290]→[5.20290:20787](∅→∅),[5.20787]→[4.158:229](∅→∅),[4.229]→[5.20828:21042](∅→∅),[5.20828]→[5.20828:21042](∅→∅),[5.21042]→[5.60:69](∅→∅)
fn load_key() -> Result<(libpijul::key::SecretKey, libpijul::key::SKey), anyhow::Error> {if let Some(mut dir) = crate::config::global_config_dir() {dir.push("secretkey.json");if let Ok(key) = std::fs::File::open(&dir) {let k: libpijul::key::SecretKey = serde_json::from_reader(key)?;let pass = if k.encryption.is_some() {Some(rpassword::read_password_from_tty(Some(&format!("Password for {:?}: ",dir)))?)} else {None};let sk = k.load(pass.as_deref())?;Ok((k, sk))} else {bail!("Secret key not found, please use `pijul key generate` and try again")}} else {bail!("Secret key not found, please use `pijul key generate` and try again")}} - edit in pijul/src/commands/log.rs at line 84
let mut global_id_path = crate::config::global_config_dir();if let Some(ref mut gl) = global_id_path {gl.push("identities")}debug!("global_id_path = {:?}", global_id_path); - edit in pijul/src/commands/log.rs at line 89
global_id_path, - edit in pijul/src/commands/log.rs at line 267
global_id_path: Option<PathBuf>, - edit in pijul/src/commands/log.rs at line 316
let mut global_id_path = self.global_id_path.clone(); - replacement in pijul/src/commands/log.rs at line 336
let entry = self.mk_log_entry(&mut authors,&mut id_path,&mut global_id_path,h.into(),Some(mrk.into()),)?;let entry =self.mk_log_entry(&mut authors, &mut id_path, h.into(), Some(mrk.into()))?; - edit in pijul/src/commands/log.rs at line 359
global_id_path: &mut Option<PathBuf>, - replacement in pijul/src/commands/log.rs at line 382
debug!("{:?} {:?}", global_id_path, id);if let Some(ref mut global_id_path) = global_id_path {if id.is_none() {global_id_path.push(e.key());debug!("{:?}", global_id_path);if let Ok(f) = std::fs::File::open(&global_id_path) {if let Ok(id_) = serde_json::from_reader(f) {id = Some(id_)} else {debug!("wrong identity for {:?}", e.key());}debug!("{:?}", id);if let Ok(identities) = crate::identity::Complete::load_all() {for identity in identities {if &identity.identity.public_key.key == e.key() {id = Some(identity.identity); - edit in pijul/src/commands/log.rs at line 388
global_id_path.pop(); - replacement in pijul/src/remote/ssh.rs at line 3
use std::path::{Path, PathBuf};use std::path::PathBuf; - replacement in pijul/src/remote/ssh.rs at line 123
self.auth_pk(&mut h, &mut key_path).await || self.auth_password(&mut h).await?let mut stderr = std::io::stderr();writeln!(stderr, "Warning: Unable to automatically authenticate with server. Please make sure your SSH keys have been uploaded to the Nest.")?;writeln!(stderr, "For more information, please visit https://pijul.org/manual/the_nest/public_keys.html#ssh-public-keys")?;self.auth_password(&mut h).await? - replacement in pijul/src/remote/ssh.rs at line 133
bail!("Not authenticated")bail!("Not authenticated. Please check your credentials and try again."); - edit in pijul/src/remote/ssh.rs at line 211[5.30396]→[5.30396:30403](∅→∅),[5.30403]→[5.344:481](∅→∅),[5.481]→[5.0:63](∅→∅),[5.63]→[5.30498:30537](∅→∅),[5.481]→[5.30498:30537](∅→∅),[5.30498]→[5.30498:30537](∅→∅),[5.30537]→[5.1618:1878](∅→∅),[5.1878]→[5.30582:30825](∅→∅),[5.30582]→[5.30582:30825](∅→∅),[5.30825]→[5.1879:1951](∅→∅),[5.1951]→[5.30898:31112](∅→∅),[5.30898]→[5.30898:31112](∅→∅)
}async fn auth_pk(&self,h: &mut thrussh::client::Handle<SshClient>,key_path: &mut PathBuf,) -> bool {if h.is_closed() {return false;}let mut authenticated = false;let mut keys = Vec::new();if let Some(ref file) = self.config.identity_file {keys.push(file.as_str())} else {keys.push("id_ed25519");keys.push("id_rsa");}for k in keys.iter() {key_path.push(k);let k = if let Some(k) = load_secret_key(&key_path, k) {k} else {key_path.pop();continue;};if let Ok(auth) = h.authenticate_publickey(&self.config.user, Arc::new(k)).await{authenticated = auth}key_path.pop();if authenticated {return true;}}false - edit in pijul/src/remote/ssh.rs at line 220[5.131]→[5.31219:31323](∅→∅),[5.617]→[5.31219:31323](∅→∅),[5.31219]→[5.31219:31323](∅→∅),[5.31323]→[5.1952:2004](∅→∅),[5.2004]→[5.31356:31370](∅→∅),[5.31356]→[5.31356:31370](∅→∅),[5.31370]→[5.2005:2093](∅→∅),[5.2093]→[5.31438:31446](∅→∅),[5.31438]→[5.31438:31446](∅→∅)
let pass = rpassword::read_password_from_tty(Some(&format!("Password for {}@{}: ",self.config.user, self.config.host_name)))?;h.authenticate_password(self.config.user.to_string(), &pass).await}} - replacement in pijul/src/remote/ssh.rs at line 221[5.31447]→[5.238:406](∅→∅),[5.406]→[5.31595:31641](∅→∅),[5.31595]→[5.31595:31641](∅→∅),[5.31641]→[5.618:679](∅→∅),[5.679]→[5.31717:32070](∅→∅),[5.31717]→[5.31717:32070](∅→∅),[5.32070]→[5.0:87](∅→∅),[5.87]→[5.32167:32203](∅→∅),[5.32167]→[5.32167:32203](∅→∅)
pub fn load_secret_key<P: AsRef<Path>>(key_path: P, k: &str) -> Option<thrussh_keys::key::KeyPair> {match thrussh_keys::load_secret_key(key_path.as_ref(), None) {Ok(k) => Some(k),Err(e) => {if let thrussh_keys::Error::KeyIsEncrypted = e {let pass = if let Ok(pass) =rpassword::read_password_from_tty(Some(&format!("Password for key {:?}: ", k))){pass} else {return None;};if pass.is_empty() {return None;}if let Ok(k) = thrussh_keys::load_secret_key(&key_path, Some(&pass)) {return Some(k);// Authentication can be attempted multiple timeslet mut authenticated = false;let username = format!("{}@{}", self.config.user, self.config.host_name);// Try authenticate using the user's keyringif let Ok(password) = keyring::Entry::new("pijul", &username).get_password() {authenticated = h.authenticate_password(self.config.user.to_string(), &password).await?;}// Try authenticate using user's passwordif !authenticated {let password = dialoguer::Password::with_theme(crate::config::load_theme().expect("Could not load config").as_ref(),).with_prompt(format!("Password for {username}")).allow_empty_password(true).interact().unwrap();authenticated = h.authenticate_password(self.config.user.to_string(), &password).await?;// If the new password is valid, update the keyring to matchif authenticated {match keyring::Entry::new("pijul", &username).set_password(&password) {Err(e) => writeln!(std::io::stderr(),"Warning: could not write new password to keychain: {e}").unwrap(),_ => (), - edit in pijul/src/remote/ssh.rs at line 259
None - edit in pijul/src/remote/ssh.rs at line 260
Ok(authenticated) - replacement in libpijul/src/key.rs at line 19
#[derive(Serialize, Deserialize)]#[derive(Clone, Debug, Serialize, Deserialize)] - replacement in libpijul/src/key.rs at line 319
#[derive(Serialize, Deserialize)]#[derive(Clone, Debug, Serialize, Deserialize)] - replacement in libpijul/src/key.rs at line 324
#[derive(Serialize, Deserialize)]#[derive(Clone, Debug, Serialize, Deserialize)]