new command `pijul client` for authenticating to a HTTP server
Dependencies
- [2]
X2MMGGXQRemoving dependencies with CVE (old version of Nix) - [3]
2MLOE3FPSolving conflicts - [4]
44RUBHREOnly re-prove identity when credentials change - [5]
MEK57BADOptional user in ssh_remote, allows to fix the key proof in `pijul id ed` - [6]
M37JTFEIRestorting SSH auth with a key - [7]
4HTHYIA3Fixing HTTP download - [8]
UMF6N7CZKeyring 2.0 - [9]
4KJ45IJLImplement new identity management - [10]
KKNMDXAITweak identity subcommand - [11]
FI3WFMTSSimplifying the locks - [12]
MU5GSJAWPartial push and pull (WARNING: breaks the existing protocol) - [13]
JL4WKA5PImplement the Sanakirja concurrency model in a cross-process way - [14]
VBMXB443Retrying if the HTTP connection drops while reading the body - [15]
LYTVEPH3Avoid cloning into an existing path - [16]
GYXIF25TProper parsing of URLs - [17]
FBXYP7QMForgot to add remote::http - [18]
QL6K2ZM3Tags - [19]
TPEH2XNB1.0.0-alpha.28, with Tokio 1.0 - [20]
OIOMXESDBetter error handling in HTTP - [21]
DO2Y5TY5Tag synchronisation - [22]
DWSAYGVEUpdate codebase to use new identity management - [23]
2K7JLB4ZNo pager on Windows - [24]
A3RM526YIntegrating identity malleability - [25]
SN7AGY6SMaking `pijul lock` robust to kill signals - [26]
IQ4FCHPZHTTP connections: pooling + retry on error - [27]
SXEYMYF7Fixing the bad changes in history (unfortunately, by rebooting). - [28]
EEBKW7VTKeys and identities - [29]
IIV3EL2XCleanup, formatting, and fixing the Git feature - [30]
YWL2K3P7Removing the `Direction` argument in pijul::remote::Repository::remote - [31]
I24UEJQLVarious post-fire fixes - [32]
4OJWMSOWFully replace crate::Identity - [33]
VL7ZYKHBRunning hooks through shell on Windows and Unix - [34]
ZHABNS3SCanonicalize all paths - [35]
6FRPUHWKFix identity tests - [36]
VAPBIG46Version bump - [37]
H4AU6QRPNew config for HTTP remotes - [*]
ZDK3GNDBTag transactions (including a massive refactoring of errors) - [*]
TI7PCK7JUpdate `pijul/src/main.rs` to use new identity management - [*]
SNZ3OAMCuse native external subcommand support instead of hand-rolled one - [*]
TEDGMEHFIntroduce subcommand for completion-script generation.
Change contents
- replacement in pijul/src/remote/ssh.rs at line 249
.with_prompt(format!("Password for encrypted private key")).allow_empty_password(false).interact().unwrap();.with_prompt(format!("Password for encrypted private key")).allow_empty_password(false).interact().unwrap(); - replacement in pijul/src/remote/ssh.rs at line 256
continuecontinue; - edit in pijul/src/remote/mod.rs at line 1121
pub async fn prove(&mut self, key: libpijul::key::SKey) -> Result<(), anyhow::Error> {match *self {RemoteRepo::Ssh(ref mut s) => s.prove(key).await,RemoteRepo::Http(ref mut h) => h.prove(key).await,RemoteRepo::None => unreachable!(),_ => Ok(()),}} - replacement in pijul/src/remote/http.rs at line 41
std::fs::create_dir_all(&path.parent().unwrap())?;tokio::fs::create_dir_all(&path.parent().unwrap()).await.unwrap(); - replacement in pijul/src/remote/http.rs at line 43
let mut f = tokio::fs::File::create(&path_).await?;let mut f = tokio::fs::File::create(&path_).await.unwrap(); - edit in pijul/src/remote/http.rs at line 75
debug!("kv = {:?} {:?}", k, v); - replacement in pijul/src/remote/http.rs at line 92
delay *= 2.;continue;bail!("Server returned {}", res.status().as_u16()) - replacement in pijul/src/remote/http.rs at line 126
debug!("renaming {:?} {:?}", c, done);debug!("renaming {:?} {:?} {:?} {:?}", c, path_, path, done); - replacement in pijul/src/remote/http.rs at line 130
std::fs::rename(&path_, &path)?;tokio::fs::rename(&path_, &path).await?; - replacement in pijul/src/remote/http.rs at line 133
std::fs::rename(&path_, &path)?;tokio::fs::rename(&path_, &path).await?; - replacement in pijul/src/remote/http.rs at line 141
const POOL_SIZE: usize = 20;const POOL_SIZE: usize = 1; - edit in pijul/src/remote/http.rs at line 272
debug!("kv = {:?} {:?}", k, v); - edit in pijul/src/remote/http.rs at line 325
debug!("kv = {:?} {:?}", k, v); - edit in pijul/src/remote/http.rs at line 343
debug!("l = {:?}", l); - edit in pijul/src/remote/http.rs at line 359
debug!("done"); - edit in pijul/src/remote/http.rs at line 384
debug!("kv = {:?} {:?}", k, v); - edit in pijul/src/remote/http.rs at line 418
debug!("kv = {:?} {:?}", k, v); - replacement in pijul/src/remote/http.rs at line 504
format!("{}", rev)rev.to_string() - replacement in pijul/src/remote/http.rs at line 506
String::new()0u32.to_string() - edit in pijul/src/remote/http.rs at line 511
debug!("kv = {:?} {:?}", k, v); - replacement in pijul/src/remote/http.rs at line 524
let resp: Identities = res.json().await?;let resp: Option<Identities> = res.json().await?; - replacement in pijul/src/remote/http.rs at line 526[9.12305]→[9.12305:12543](∅→∅),[9.12543]→[9.266:342](∅→∅),[9.342]→[9.12605:12629](∅→∅),[9.12605]→[9.12605:12629](∅→∅)
std::fs::create_dir_all(&path)?;for id in resp.id.iter() {path.push(&id.public_key.key);debug!("recv identity: {:?} {:?}", id, path);let mut id_file = std::fs::File::create(&path)?;serde_json::to_writer_pretty(&mut id_file, &id.as_portable())?;path.pop();if let Some(resp) = resp {std::fs::create_dir_all(&path)?;for id in resp.id.iter() {path.push(&id.public_key.key);debug!("recv identity: {:?} {:?}", id, path);let mut id_file = std::fs::File::create(&path)?;serde_json::to_writer_pretty(&mut id_file, &id.as_portable())?;path.pop();}Ok(resp.rev)} else {Ok(0) - replacement in pijul/src/remote/http.rs at line 539
Ok(resp.rev)}pub async fn prove(&mut self,key: libpijul::key::SKey,) -> Result<(), anyhow::Error> {debug!("prove {:?}", self.url);let url = format!("{}/{}", self.url, super::DOT_DIR);let q = [("challenge", key.public_key().key)];let mut req = self.client.get(&url).query(&q).header(reqwest::header::USER_AGENT, USER_AGENT);for (k, v) in self.headers.iter() {debug!("kv = {:?} {:?}", k, v);req = req.header(k.as_str(), v.as_str());}let res = req.send().await?;if !res.status().is_success() {bail!("HTTP error {:?}", res.status())}let resp = res.bytes().await?;debug!("resp = {:?}", resp);let sig = key.sign_raw(&resp)?;debug!("sig = {:?}", sig);let q = [("prove", &sig)];let mut req = self.client.get(&url).query(&q).header(reqwest::header::USER_AGENT, USER_AGENT);for (k, v) in self.headers.iter() {debug!("kv = {:?} {:?}", k, v);req = req.header(k.as_str(), v.as_str());}let res = req.send().await?;if !res.status().is_success() {bail!("HTTP error {:?}", res.status())}Ok(()) - edit in pijul/src/main.rs at line 136
/// Authenticates with a HTTP server.Client(Client), - edit in pijul/src/main.rs at line 272
SubCommand::Client(client) => client.run().await, - replacement in pijul/src/identity/create.rs at line 125
.prove(*NO_CERT_CHECK.get_or_init(|| false)).prove(None, *NO_CERT_CHECK.get_or_init(|| false)) - replacement in pijul/src/identity/create.rs at line 300
pub async fn prove(&self, no_cert_check: bool) -> Result<(), anyhow::Error> {pub async fn prove(&self,origin: Option<&str>,no_cert_check: bool,) -> Result<(), anyhow::Error> {let remote = origin.unwrap_or(&self.config.author.origin); - replacement in pijul/src/identity/create.rs at line 310
&self.name, &self.config.author.username, &self.config.author.origin&self.name, &self.config.author.username, remote - edit in pijul/src/identity/create.rs at line 313
let remote = self.config.author.origin.clone(); - replacement in pijul/src/identity/create.rs at line 314[9.37291]→[9.37291:37432](∅→∅),[9.37432]→[5.746:802](∅→∅),[5.802]→[9.37432:37505](∅→∅),[9.37432]→[9.37432:37505](∅→∅),[9.37542]→[9.37542:37767](∅→∅),[9.37767]→[5.803:926](∅→∅),[5.926]→[9.37854:38042](∅→∅),[9.37854]→[9.37854:38042](∅→∅)
use crate::remote::RemoteRepo;if let RemoteRepo::Ssh(ssh) = repo.remote(None,Some(&self.config.author.username),&remote,crate::DEFAULT_CHANNEL,no_cert_check,false,).await?{ssh} else {bail!("No such remote: {}", remote)}} else if let Some(mut ssh) = crate::remote::ssh::ssh_remote(Some(&self.config.author.username), &remote, false) {if let Some(c) = ssh.connect(&remote, crate::DEFAULT_CHANNEL).await? {c} else {bail!("No such remote: {}", remote)}repo.remote(None,Some(&self.config.author.username),&remote,crate::DEFAULT_CHANNEL,no_cert_check,false,).await? - replacement in pijul/src/identity/create.rs at line 324
bail!("No such remote: {}", remote)crate::remote::unknown_remote(None,Some(&self.config.author.username),&remote,crate::DEFAULT_CHANNEL,no_cert_check,false,).await? - replacement in pijul/src/config.rs at line 206
Ok(String::from_utf8(out.stdout)?)Ok(String::from_utf8(out.stdout)?.trim().to_string()) - edit in pijul/src/commands/mod.rs at line 65
// #[cfg(debug_assertions)] - edit in pijul/src/commands/mod.rs at line 66
// #[cfg(debug_assertions)] - edit in pijul/src/commands/mod.rs at line 68[9.134333][42.138]
mod client;pub use client::*; - edit in pijul/src/commands/identity.rs at line 167
/// Set the target serverserver: Option<String>, - edit in pijul/src/commands/identity.rs at line 268
server, - replacement in pijul/src/commands/identity.rs at line 272
.prove(self.no_cert_check).prove(server.as_deref(), self.no_cert_check) - file addition: client.rs[9.93386]
use clap::Parser;use std::convert::Infallible;use std::net::SocketAddr;use hyper::{Body, Request, Response, Server};use hyper::service::{make_service_fn, service_fn};use tokio::sync::mpsc::channel;use tokio::select;use crate::config::global_config_dir;#[derive(Parser, Debug)]pub struct Client {/// Url to authenticate to.#[clap(value_name = "URL")]url: String,}impl Client {pub async fn run(self) -> Result<(), anyhow::Error> {let url = url::Url::parse(&self.url)?;let mut cache_path = None;if let Some(mut cached) = global_config_dir() {cached.push("cache");if let Some(host) = url.host_str() {std::fs::create_dir_all(&cached)?;cached.push(host);if let Ok(token) = std::fs::read_to_string(&cached) {println!("Bearer {}", token);return Ok(())} else {cache_path = Some(cached);}}}let (tx, mut rx) = channel::<String>(1);let make_service = make_service_fn(|_conn| {let tx = tx.clone();async move {let handle = move |req: Request<_>| {let qq: Option<String> = if let Some(q) = req.uri().query() {let eq = "token=";if q.starts_with(eq) {Some(q.split_at(eq.len()).1.to_string())} else {None}} else {None};let tx = tx.clone();async move {if let Some(qq) = qq {tx.send(qq).await.unwrap();let resp = Response::builder().header("Content-Type", "text/html").body(Body::from(include_str!("client.html"))).unwrap();Ok::<_, Infallible>(resp)} else {Ok::<_, Infallible>(Response::builder().status(404).body("Not found".into()).unwrap())}}};Ok::<_, Infallible>(service_fn(handle))}});let mut port = 3000;loop {let addr = SocketAddr::from(([127, 0, 0, 1], port));if let Ok(server) = Server::try_bind(&addr) {let mut url = url::Url::parse(&self.url)?;url.query_pairs_mut().append_pair("port", &port.to_string());open::that(&url.to_string())?;let server = server.serve(make_service);select!{x = server => {if let Err(e) = x {eprintln!("server error: {}", e);}break}x = rx.recv() => {if let Some(x) = x {if let Some(cache_path) = cache_path {std::fs::write(&cache_path, &x)?;}println!("Bearer {}", x);}break}}}if port < u16::MAX {port += 1} else {break}}Ok(())}} - edit in pijul/Cargo.toml at line 77
hyper = { version = "0.14", features = [ "server" ] } - replacement in pijul/Cargo.toml at line 98
url = "2.2"url = "2.3" - edit in pijul/Cargo.toml at line 111
open = "3"bs58 = "0.4"