From 0dffe3f99aa062983f2a023d6d7df7d7f61e5d33 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Wed, 13 Feb 2019 14:31:43 +0100 Subject: [PATCH] src/client/http_client.rs: try to login use an environment var to store passphrase (PBS_PASSWORD) --- src/bin/proxmox-backup-client.rs | 4 +-- src/client/http_client.rs | 46 +++++++++++++++++++++++++++++++- src/server/rest.rs | 4 ++- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs index 962805b9..8bbaa0f0 100644 --- a/src/bin/proxmox-backup-client.rs +++ b/src/bin/proxmox-backup-client.rs @@ -53,7 +53,7 @@ impl BackupRepository { fn backup_directory(repo: &BackupRepository, body: Body, archive_name: &str) -> Result<(), Error> { - let client = HttpClient::new(&repo.host); + let client = HttpClient::new(&repo.host, &repo.user); let epoch = std::time::SystemTime::now().duration_since( std::time::SystemTime::UNIX_EPOCH)?.as_secs(); @@ -107,7 +107,7 @@ fn list_backups( let repo_url = tools::required_string_param(¶m, "repository")?; let repo = BackupRepository::parse(repo_url)?; - let client = HttpClient::new(&repo.host); + let client = HttpClient::new(&repo.host, &repo.user); let path = format!("api2/json/admin/datastore/{}/backups", repo.store); diff --git a/src/client/http_client.rs b/src/client/http_client.rs index d90407e4..3b76b454 100644 --- a/src/client/http_client.rs +++ b/src/client/http_client.rs @@ -9,16 +9,19 @@ use http::Request; use futures::stream::Stream; use serde_json::{Value}; +use url::percent_encoding::{percent_encode, DEFAULT_ENCODE_SET}; pub struct HttpClient { + username: String, server: String, } impl HttpClient { - pub fn new(server: &str) -> Self { + pub fn new(server: &str, username: &str) -> Self { Self { server: String::from(server), + username: String::from(username), } } @@ -77,23 +80,64 @@ impl HttpClient { let url: Uri = format!("https://{}:8007/{}", self.server, path).parse()?; + let ticket = self.login()?; + + let enc_ticket = percent_encode(ticket.as_bytes(), DEFAULT_ENCODE_SET).to_string(); + let request = Request::builder() .method("GET") .uri(url) .header("User-Agent", "proxmox-backup-client/1.0") + .header("Cookie", format!("PBSAuthCookie={}", enc_ticket)) .body(Body::empty())?; Self::run_request(request) } + fn login(&self) -> Result { + + let url: Uri = format!("https://{}:8007/{}", self.server, "/api2/json/access/ticket").parse()?; + + let password = match std::env::var("PBS_PASSWORD") { + Ok(p) => p, + Err(err) => bail!("missing passphrase - {}", err), + }; + + let query = url::form_urlencoded::Serializer::new(String::new()) + .append_pair("username", &self.username) + .append_pair("password", &password) + .finish(); + + let request = Request::builder() + .method("POST") + .uri(url) + .header("User-Agent", "proxmox-backup-client/1.0") + .header("Content-Type", "application/x-www-form-urlencoded") + .body(Body::from(query))?; + + let auth_res = Self::run_request(request)?; + + let ticket = match auth_res["data"]["ticket"].as_str() { + Some(t) => t, + None => bail!("got unexpected respose for login request."), + }; + + Ok(ticket.to_owned()) + } + pub fn upload(&self, content_type: &str, body: Body, path: &str) -> Result { let url: Uri = format!("https://{}:8007/{}", self.server, path).parse()?; + let ticket = self.login()?; + + let enc_ticket = percent_encode(ticket.as_bytes(), DEFAULT_ENCODE_SET).to_string(); + let request = Request::builder() .method("POST") .uri(url) .header("User-Agent", "proxmox-backup-client/1.0") + .header("Cookie", format!("PBSAuthCookie={}", enc_ticket)) .header("Content-Type", content_type) .body(body)?; diff --git a/src/server/rest.rs b/src/server/rest.rs index 1c63c3e9..49e4a8b4 100644 --- a/src/server/rest.rs +++ b/src/server/rest.rs @@ -70,7 +70,7 @@ impl Service for ApiService { match result { Ok(res) => Ok::<_, hyper::Error>(res), Err(err) => { - if let Some(apierr) = err.downcast_ref::() { + if let Some(apierr) = err.downcast_ref::() { let mut resp = Response::new(Body::from(apierr.message.clone())); *resp.status_mut() = apierr.code; Ok(resp) @@ -458,6 +458,8 @@ pub fn handle_request(api: Arc, req: Request) -> BoxFut { if let Some(_username) = rpcenv.get_user() { // fixme: check permissions } else { + println!("Abort UNAUTHORIZED API REQUEST"); + // always delay unauthorized calls by 3 seconds (from start of request) let resp = (formatter.format_error)(http_err!(UNAUTHORIZED, "permission check failed.".into())); let delayed_response = tokio::timer::Delay::new(delay_unauth_time)