From: "Fabian Grünbichler" <f.gruenbichler@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup 08/16] client/remote: allow using ApiToken + secret
Date: Wed, 28 Oct 2020 12:36:31 +0100 [thread overview]
Message-ID: <20201028113632.814586-11-f.gruenbichler@proxmox.com> (raw)
In-Reply-To: <20201028113632.814586-1-f.gruenbichler@proxmox.com>
in place of user + password.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
Notes:
this will require more follow-up to disentagle the CLI args
properly, but it allows using tokens with the client/pull as is.
examples/download-speed.rs | 6 +-
examples/upload-speed.rs | 6 +-
src/api2/config/remote.rs | 2 +-
src/api2/pull.rs | 2 +-
src/api2/types/mod.rs | 2 +-
src/bin/proxmox-backup-client.rs | 32 +++++-----
src/bin/proxmox-backup-manager.rs | 4 +-
src/bin/proxmox_backup_client/benchmark.rs | 2 +-
src/bin/proxmox_backup_client/catalog.rs | 4 +-
src/bin/proxmox_backup_client/mount.rs | 2 +-
src/bin/proxmox_backup_client/task.rs | 8 +--
src/client/backup_repo.rs | 25 +++++---
src/client/http_client.rs | 68 +++++++++++++++-------
src/client/pull.rs | 2 +-
src/config/remote.rs | 2 +-
15 files changed, 101 insertions(+), 66 deletions(-)
diff --git a/examples/download-speed.rs b/examples/download-speed.rs
index fa278436..3ccf4ce7 100644
--- a/examples/download-speed.rs
+++ b/examples/download-speed.rs
@@ -2,7 +2,7 @@ use std::io::Write;
use anyhow::{Error};
-use proxmox_backup::api2::types::Userid;
+use proxmox_backup::api2::types::Authid;
use proxmox_backup::client::{HttpClient, HttpClientOptions, BackupReader};
pub struct DummyWriter {
@@ -26,13 +26,13 @@ async fn run() -> Result<(), Error> {
let host = "localhost";
- let username = Userid::root_userid();
+ let auth_id = Authid::root_auth_id();
let options = HttpClientOptions::new()
.interactive(true)
.ticket_cache(true);
- let client = HttpClient::new(host, 8007, username, options)?;
+ let client = HttpClient::new(host, 8007, auth_id, options)?;
let backup_time = proxmox::tools::time::parse_rfc3339("2019-06-28T10:49:48Z")?;
diff --git a/examples/upload-speed.rs b/examples/upload-speed.rs
index d26e6db3..641ed952 100644
--- a/examples/upload-speed.rs
+++ b/examples/upload-speed.rs
@@ -1,6 +1,6 @@
use anyhow::{Error};
-use proxmox_backup::api2::types::Userid;
+use proxmox_backup::api2::types::Authid;
use proxmox_backup::client::*;
async fn upload_speed() -> Result<f64, Error> {
@@ -8,13 +8,13 @@ async fn upload_speed() -> Result<f64, Error> {
let host = "localhost";
let datastore = "store2";
- let username = Userid::root_userid();
+ let auth_id = Authid::root_auth_id();
let options = HttpClientOptions::new()
.interactive(true)
.ticket_cache(true);
- let client = HttpClient::new(host, 8007, username, options)?;
+ let client = HttpClient::new(host, 8007, auth_id, options)?;
let backup_time = proxmox::tools::time::epoch_i64();
diff --git a/src/api2/config/remote.rs b/src/api2/config/remote.rs
index dd2777c9..96869695 100644
--- a/src/api2/config/remote.rs
+++ b/src/api2/config/remote.rs
@@ -201,7 +201,7 @@ pub fn update_remote(
comment: Option<String>,
host: Option<String>,
port: Option<u16>,
- userid: Option<Userid>,
+ userid: Option<Authid>,
password: Option<String>,
fingerprint: Option<String>,
delete: Option<Vec<DeletableProperty>>,
diff --git a/src/api2/pull.rs b/src/api2/pull.rs
index aef7de4e..d70fe014 100644
--- a/src/api2/pull.rs
+++ b/src/api2/pull.rs
@@ -56,7 +56,7 @@ pub async fn get_pull_parameters(
let src_repo = BackupRepository::new(Some(remote.userid.clone()), Some(remote.host.clone()), remote.port, remote_store.to_string());
- let client = HttpClient::new(&src_repo.host(), src_repo.port(), &src_repo.user(), options)?;
+ let client = HttpClient::new(&src_repo.host(), src_repo.port(), &src_repo.auth_id(), options)?;
let _auth_info = client.login() // make sure we can auth
.await
.map_err(|err| format_err!("remote connection to '{}' failed - {}", remote.host, err))?;
diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs
index b1287583..4084112d 100644
--- a/src/api2/types/mod.rs
+++ b/src/api2/types/mod.rs
@@ -67,7 +67,7 @@ const_regex!{
pub DNS_NAME_OR_IP_REGEX = concat!(r"^(?:", DNS_NAME!(), "|", IPRE!(), r")$");
- pub BACKUP_REPO_URL_REGEX = concat!(r"^^(?:(?:(", USER_ID_REGEX_STR!(), ")@)?(", DNS_NAME!(), "|", IPRE_BRACKET!() ,"):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$");
+ pub BACKUP_REPO_URL_REGEX = concat!(r"^^(?:(?:(", USER_ID_REGEX_STR!(), "|", APITOKEN_ID_REGEX_STR!(), ")@)?(", DNS_NAME!(), "|", IPRE_BRACKET!() ,"):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$");
pub CERT_FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs
index 8c68ffd2..ce263a77 100644
--- a/src/bin/proxmox-backup-client.rs
+++ b/src/bin/proxmox-backup-client.rs
@@ -193,7 +193,7 @@ pub fn complete_repository(_arg: &str, _param: &HashMap<String, String>) -> Vec<
result
}
-fn connect(server: &str, port: u16, userid: &Userid) -> Result<HttpClient, Error> {
+fn connect(server: &str, port: u16, auth_id: &Authid) -> Result<HttpClient, Error> {
let fingerprint = std::env::var(ENV_VAR_PBS_FINGERPRINT).ok();
@@ -212,7 +212,7 @@ fn connect(server: &str, port: u16, userid: &Userid) -> Result<HttpClient, Error
.fingerprint_cache(true)
.ticket_cache(true);
- HttpClient::new(server, port, userid, options)
+ HttpClient::new(server, port, auth_id, options)
}
async fn view_task_result(
@@ -366,7 +366,7 @@ async fn list_backup_groups(param: Value) -> Result<Value, Error> {
let repo = extract_repository_from_value(¶m)?;
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
let path = format!("api2/json/admin/datastore/{}/groups", repo.store());
@@ -435,7 +435,7 @@ async fn change_backup_owner(group: String, mut param: Value) -> Result<(), Erro
let repo = extract_repository_from_value(¶m)?;
- let mut client = connect(repo.host(), repo.port(), repo.user())?;
+ let mut client = connect(repo.host(), repo.port(), repo.auth_id())?;
param.as_object_mut().unwrap().remove("repository");
@@ -478,7 +478,7 @@ async fn list_snapshots(param: Value) -> Result<Value, Error> {
let output_format = get_output_format(¶m);
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
let group: Option<BackupGroup> = if let Some(path) = param["group"].as_str() {
Some(path.parse()?)
@@ -543,7 +543,7 @@ async fn forget_snapshots(param: Value) -> Result<Value, Error> {
let path = tools::required_string_param(¶m, "snapshot")?;
let snapshot: BackupDir = path.parse()?;
- let mut client = connect(repo.host(), repo.port(), repo.user())?;
+ let mut client = connect(repo.host(), repo.port(), repo.auth_id())?;
let path = format!("api2/json/admin/datastore/{}/snapshots", repo.store());
@@ -573,7 +573,7 @@ async fn api_login(param: Value) -> Result<Value, Error> {
let repo = extract_repository_from_value(¶m)?;
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
client.login().await?;
record_repository(&repo);
@@ -630,7 +630,7 @@ async fn api_version(param: Value) -> Result<(), Error> {
let repo = extract_repository_from_value(¶m);
if let Ok(repo) = repo {
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
match client.get("api2/json/version", None).await {
Ok(mut result) => version_info["server"] = result["data"].take(),
@@ -680,7 +680,7 @@ async fn list_snapshot_files(param: Value) -> Result<Value, Error> {
let output_format = get_output_format(¶m);
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
let path = format!("api2/json/admin/datastore/{}/files", repo.store());
@@ -724,7 +724,7 @@ async fn start_garbage_collection(param: Value) -> Result<Value, Error> {
let output_format = get_output_format(¶m);
- let mut client = connect(repo.host(), repo.port(), repo.user())?;
+ let mut client = connect(repo.host(), repo.port(), repo.auth_id())?;
let path = format!("api2/json/admin/datastore/{}/gc", repo.store());
@@ -1036,7 +1036,7 @@ async fn create_backup(
let backup_time = backup_time_opt.unwrap_or_else(|| epoch_i64());
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
record_repository(&repo);
println!("Starting backup: {}/{}/{}", backup_type, backup_id, BackupDir::backup_time_to_string(backup_time)?);
@@ -1339,7 +1339,7 @@ async fn restore(param: Value) -> Result<Value, Error> {
let archive_name = tools::required_string_param(¶m, "archive-name")?;
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
record_repository(&repo);
@@ -1512,7 +1512,7 @@ async fn upload_log(param: Value) -> Result<Value, Error> {
let snapshot = tools::required_string_param(¶m, "snapshot")?;
let snapshot: BackupDir = snapshot.parse()?;
- let mut client = connect(repo.host(), repo.port(), repo.user())?;
+ let mut client = connect(repo.host(), repo.port(), repo.auth_id())?;
let (keydata, crypt_mode) = keyfile_parameters(¶m)?;
@@ -1583,7 +1583,7 @@ fn prune<'a>(
async fn prune_async(mut param: Value) -> Result<Value, Error> {
let repo = extract_repository_from_value(¶m)?;
- let mut client = connect(repo.host(), repo.port(), repo.user())?;
+ let mut client = connect(repo.host(), repo.port(), repo.auth_id())?;
let path = format!("api2/json/admin/datastore/{}/prune", repo.store());
@@ -1666,7 +1666,7 @@ async fn status(param: Value) -> Result<Value, Error> {
let output_format = get_output_format(¶m);
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
let path = format!("api2/json/admin/datastore/{}/status", repo.store());
@@ -1711,7 +1711,7 @@ async fn try_get(repo: &BackupRepository, url: &str) -> Value {
.fingerprint_cache(true)
.ticket_cache(true);
- let client = match HttpClient::new(repo.host(), repo.port(), repo.user(), options) {
+ let client = match HttpClient::new(repo.host(), repo.port(), repo.auth_id(), options) {
Ok(v) => v,
_ => return Value::Null,
};
diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs
index f13e55ee..549be2ef 100644
--- a/src/bin/proxmox-backup-manager.rs
+++ b/src/bin/proxmox-backup-manager.rs
@@ -62,10 +62,10 @@ fn connect() -> Result<HttpClient, Error> {
let ticket = Ticket::new("PBS", Userid::root_userid())?
.sign(private_auth_key(), None)?;
options = options.password(Some(ticket));
- HttpClient::new("localhost", 8007, Userid::root_userid(), options)?
+ HttpClient::new("localhost", 8007, Authid::root_auth_id(), options)?
} else {
options = options.ticket_cache(true).interactive(true);
- HttpClient::new("localhost", 8007, Userid::root_userid(), options)?
+ HttpClient::new("localhost", 8007, Authid::root_auth_id(), options)?
};
Ok(client)
diff --git a/src/bin/proxmox_backup_client/benchmark.rs b/src/bin/proxmox_backup_client/benchmark.rs
index 37bb87fb..b434956d 100644
--- a/src/bin/proxmox_backup_client/benchmark.rs
+++ b/src/bin/proxmox_backup_client/benchmark.rs
@@ -225,7 +225,7 @@ async fn test_upload_speed(
let backup_time = proxmox::tools::time::epoch_i64();
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
record_repository(&repo);
if verbose { eprintln!("Connecting to backup server"); }
diff --git a/src/bin/proxmox_backup_client/catalog.rs b/src/bin/proxmox_backup_client/catalog.rs
index e35692f2..87d80f3b 100644
--- a/src/bin/proxmox_backup_client/catalog.rs
+++ b/src/bin/proxmox_backup_client/catalog.rs
@@ -79,7 +79,7 @@ async fn dump_catalog(param: Value) -> Result<Value, Error> {
}
};
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
let client = BackupReader::start(
client,
@@ -153,7 +153,7 @@ async fn dump_catalog(param: Value) -> Result<Value, Error> {
/// Shell to interactively inspect and restore snapshots.
async fn catalog_shell(param: Value) -> Result<(), Error> {
let repo = extract_repository_from_value(¶m)?;
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
let path = tools::required_string_param(¶m, "snapshot")?;
let archive_name = tools::required_string_param(¶m, "archive-name")?;
diff --git a/src/bin/proxmox_backup_client/mount.rs b/src/bin/proxmox_backup_client/mount.rs
index 69ab6d3f..6c91c132 100644
--- a/src/bin/proxmox_backup_client/mount.rs
+++ b/src/bin/proxmox_backup_client/mount.rs
@@ -163,7 +163,7 @@ fn mount(
async fn mount_do(param: Value, pipe: Option<RawFd>) -> Result<Value, Error> {
let repo = extract_repository_from_value(¶m)?;
let archive_name = tools::required_string_param(¶m, "archive-name")?;
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
let target = param["target"].as_str();
diff --git a/src/bin/proxmox_backup_client/task.rs b/src/bin/proxmox_backup_client/task.rs
index 72d8095c..3bf817d8 100644
--- a/src/bin/proxmox_backup_client/task.rs
+++ b/src/bin/proxmox_backup_client/task.rs
@@ -48,7 +48,7 @@ async fn task_list(param: Value) -> Result<Value, Error> {
let output_format = get_output_format(¶m);
let repo = extract_repository_from_value(¶m)?;
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
let limit = param["limit"].as_u64().unwrap_or(50) as usize;
let running = !param["all"].as_bool().unwrap_or(false);
@@ -57,7 +57,7 @@ async fn task_list(param: Value) -> Result<Value, Error> {
"running": running,
"start": 0,
"limit": limit,
- "userfilter": repo.user(),
+ "userfilter": repo.auth_id(),
"store": repo.store(),
});
@@ -96,7 +96,7 @@ async fn task_log(param: Value) -> Result<Value, Error> {
let repo = extract_repository_from_value(¶m)?;
let upid = tools::required_string_param(¶m, "upid")?;
- let client = connect(repo.host(), repo.port(), repo.user())?;
+ let client = connect(repo.host(), repo.port(), repo.auth_id())?;
display_task_log(client, upid, true).await?;
@@ -122,7 +122,7 @@ async fn task_stop(param: Value) -> Result<Value, Error> {
let repo = extract_repository_from_value(¶m)?;
let upid_str = tools::required_string_param(¶m, "upid")?;
- let mut client = connect(repo.host(), repo.port(), repo.user())?;
+ let mut client = connect(repo.host(), repo.port(), repo.auth_id())?;
let path = format!("api2/json/nodes/localhost/tasks/{}", upid_str);
let _ = client.delete(&path, None).await?;
diff --git a/src/client/backup_repo.rs b/src/client/backup_repo.rs
index c33314bf..091d2707 100644
--- a/src/client/backup_repo.rs
+++ b/src/client/backup_repo.rs
@@ -16,7 +16,7 @@ pub const BACKUP_REPO_URL: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_RE
#[derive(Debug)]
pub struct BackupRepository {
/// The user name used for Authentication
- user: Option<Userid>,
+ auth_id: Option<Authid>,
/// The host name or IP address
host: Option<String>,
/// The port
@@ -27,20 +27,29 @@ pub struct BackupRepository {
impl BackupRepository {
- pub fn new(user: Option<Userid>, host: Option<String>, port: Option<u16>, store: String) -> Self {
+ pub fn new(auth_id: Option<Authid>, host: Option<String>, port: Option<u16>, store: String) -> Self {
let host = match host {
Some(host) if (IP_V6_REGEX.regex_obj)().is_match(&host) => {
Some(format!("[{}]", host))
},
other => other,
};
- Self { user, host, port, store }
+ Self { auth_id, host, port, store }
+ }
+
+ pub fn auth_id(&self) -> &Authid {
+ if let Some(ref auth_id) = self.auth_id {
+ return auth_id;
+ }
+
+ &Authid::root_auth_id()
}
pub fn user(&self) -> &Userid {
- if let Some(ref user) = self.user {
- return &user;
+ if let Some(auth_id) = &self.auth_id {
+ return auth_id.user();
}
+
Userid::root_userid()
}
@@ -65,8 +74,8 @@ impl BackupRepository {
impl fmt::Display for BackupRepository {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match (&self.user, &self.host, self.port) {
- (Some(user), _, _) => write!(f, "{}@{}:{}:{}", user, self.host(), self.port(), self.store),
+ match (&self.auth_id, &self.host, self.port) {
+ (Some(auth_id), _, _) => write!(f, "{}@{}:{}:{}", auth_id, self.host(), self.port(), self.store),
(None, Some(host), None) => write!(f, "{}:{}", host, self.store),
(None, _, Some(port)) => write!(f, "{}:{}:{}", self.host(), port, self.store),
(None, None, None) => write!(f, "{}", self.store),
@@ -88,7 +97,7 @@ impl std::str::FromStr for BackupRepository {
.ok_or_else(|| format_err!("unable to parse repository url '{}'", url))?;
Ok(Self {
- user: cap.get(1).map(|m| Userid::try_from(m.as_str().to_owned())).transpose()?,
+ auth_id: cap.get(1).map(|m| Authid::try_from(m.as_str().to_owned())).transpose()?,
host: cap.get(2).map(|m| m.as_str().to_owned()),
port: cap.get(3).map(|m| m.as_str().parse::<u16>()).transpose()?,
store: cap[4].to_owned(),
diff --git a/src/client/http_client.rs b/src/client/http_client.rs
index b57630f8..3b7597fe 100644
--- a/src/client/http_client.rs
+++ b/src/client/http_client.rs
@@ -21,7 +21,7 @@ use proxmox::{
};
use super::pipe_to_stream::PipeToSendStream;
-use crate::api2::types::Userid;
+use crate::api2::types::{Authid, Userid};
use crate::tools::{
self,
BroadcastFuture,
@@ -31,7 +31,7 @@ use crate::tools::{
#[derive(Clone)]
pub struct AuthInfo {
- pub userid: Userid,
+ pub auth_id: Authid,
pub ticket: String,
pub token: String,
}
@@ -102,7 +102,7 @@ pub struct HttpClient {
server: String,
port: u16,
fingerprint: Arc<Mutex<Option<String>>>,
- first_auth: BroadcastFuture<()>,
+ first_auth: Option<BroadcastFuture<()>>,
auth: Arc<RwLock<AuthInfo>>,
ticket_abort: futures::future::AbortHandle,
_options: HttpClientOptions,
@@ -251,7 +251,7 @@ impl HttpClient {
pub fn new(
server: &str,
port: u16,
- userid: &Userid,
+ auth_id: &Authid,
mut options: HttpClientOptions,
) -> Result<Self, Error> {
@@ -311,6 +311,11 @@ impl HttpClient {
let password = if let Some(password) = password {
password
} else {
+ let userid = if auth_id.is_token() {
+ bail!("API token secret must be provided!");
+ } else {
+ auth_id.user()
+ };
let mut ticket_info = None;
if use_ticket_cache {
ticket_info = load_ticket_info(options.prefix.as_ref().unwrap(), server, userid);
@@ -323,7 +328,7 @@ impl HttpClient {
};
let auth = Arc::new(RwLock::new(AuthInfo {
- userid: userid.clone(),
+ auth_id: auth_id.clone(),
ticket: password.clone(),
token: "".to_string(),
}));
@@ -336,14 +341,14 @@ impl HttpClient {
let renewal_future = async move {
loop {
tokio::time::delay_for(Duration::new(60*15, 0)).await; // 15 minutes
- let (userid, ticket) = {
+ let (auth_id, ticket) = {
let authinfo = auth2.read().unwrap().clone();
- (authinfo.userid, authinfo.ticket)
+ (authinfo.auth_id, authinfo.ticket)
};
- match Self::credentials(client2.clone(), server2.clone(), port, userid, ticket).await {
+ match Self::credentials(client2.clone(), server2.clone(), port, auth_id.user().clone(), ticket).await {
Ok(auth) => {
if use_ticket_cache & &prefix2.is_some() {
- let _ = store_ticket_info(prefix2.as_ref().unwrap(), &server2, &auth.userid.to_string(), &auth.ticket, &auth.token);
+ let _ = store_ticket_info(prefix2.as_ref().unwrap(), &server2, &auth.auth_id.to_string(), &auth.ticket, &auth.token);
}
*auth2.write().unwrap() = auth;
},
@@ -361,7 +366,7 @@ impl HttpClient {
client.clone(),
server.to_owned(),
port,
- userid.to_owned(),
+ auth_id.user().clone(),
password.to_owned(),
).map_ok({
let server = server.to_string();
@@ -370,13 +375,20 @@ impl HttpClient {
move |auth| {
if use_ticket_cache & &prefix.is_some() {
- let _ = store_ticket_info(prefix.as_ref().unwrap(), &server, &auth.userid.to_string(), &auth.ticket, &auth.token);
+ let _ = store_ticket_info(prefix.as_ref().unwrap(), &server, &auth.auth_id.to_string(), &auth.ticket, &auth.token);
}
*authinfo.write().unwrap() = auth;
tokio::spawn(renewal_future);
}
});
+ let first_auth = if auth_id.is_token() {
+ // TODO check access here?
+ None
+ } else {
+ Some(BroadcastFuture::new(Box::new(login_future)))
+ };
+
Ok(Self {
client,
server: String::from(server),
@@ -384,7 +396,7 @@ impl HttpClient {
fingerprint: verified_fingerprint,
auth,
ticket_abort,
- first_auth: BroadcastFuture::new(Box::new(login_future)),
+ first_auth,
_options: options,
})
}
@@ -394,7 +406,10 @@ impl HttpClient {
/// Login is done on demand, so this is only required if you need
/// access to authentication data in 'AuthInfo'.
pub async fn login(&self) -> Result<AuthInfo, Error> {
- self.first_auth.listen().await?;
+ if let Some(future) = &self.first_auth {
+ future.listen().await?;
+ }
+
let authinfo = self.auth.read().unwrap();
Ok(authinfo.clone())
}
@@ -477,10 +492,14 @@ impl HttpClient {
let client = self.client.clone();
let auth = self.login().await?;
-
- let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
- req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap());
- req.headers_mut().insert("CSRFPreventionToken", HeaderValue::from_str(&auth.token).unwrap());
+ if auth.auth_id.is_token() {
+ let enc_api_token = format!("{}:{}", auth.auth_id, percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
+ req.headers_mut().insert("Authorization", HeaderValue::from_str(&enc_api_token).unwrap());
+ } else {
+ let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
+ req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap());
+ req.headers_mut().insert("CSRFPreventionToken", HeaderValue::from_str(&auth.token).unwrap());
+ }
Self::api_request(client, req).await
}
@@ -579,11 +598,18 @@ impl HttpClient {
protocol_name: String,
) -> Result<(H2Client, futures::future::AbortHandle), Error> {
- let auth = self.login().await?;
let client = self.client.clone();
+ let auth = self.login().await?;
+
+ if auth.auth_id.is_token() {
+ let enc_api_token = format!("{}:{}", auth.auth_id, percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
+ req.headers_mut().insert("Authorization", HeaderValue::from_str(&enc_api_token).unwrap());
+ } else {
+ let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
+ req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap());
+ req.headers_mut().insert("CSRFPreventionToken", HeaderValue::from_str(&auth.token).unwrap());
+ }
- let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
- req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap());
req.headers_mut().insert("UPGRADE", HeaderValue::from_str(&protocol_name).unwrap());
let resp = client.request(req).await?;
@@ -636,7 +662,7 @@ impl HttpClient {
let req = Self::request_builder(&server, port, "POST", "/api2/json/access/ticket", Some(data))?;
let cred = Self::api_request(client, req).await?;
let auth = AuthInfo {
- userid: cred["data"]["username"].as_str().unwrap().parse()?,
+ auth_id: cred["data"]["username"].as_str().unwrap().parse()?,
ticket: cred["data"]["ticket"].as_str().unwrap().to_owned(),
token: cred["data"]["CSRFPreventionToken"].as_str().unwrap().to_owned(),
};
diff --git a/src/client/pull.rs b/src/client/pull.rs
index 7cb8d8e1..a80b10dc 100644
--- a/src/client/pull.rs
+++ b/src/client/pull.rs
@@ -451,7 +451,7 @@ pub async fn pull_group(
.password(Some(auth_info.ticket.clone()))
.fingerprint(fingerprint.clone());
- let new_client = HttpClient::new(src_repo.host(), src_repo.port(), src_repo.user(), options)?;
+ let new_client = HttpClient::new(src_repo.host(), src_repo.port(), src_repo.auth_id(), options)?;
let reader = BackupReader::start(
new_client,
diff --git a/src/config/remote.rs b/src/config/remote.rs
index 7ad653ac..14c57c0e 100644
--- a/src/config/remote.rs
+++ b/src/config/remote.rs
@@ -65,7 +65,7 @@ pub struct Remote {
pub host: String,
#[serde(skip_serializing_if="Option::is_none")]
pub port: Option<u16>,
- pub userid: Userid,
+ pub userid: Authid,
#[serde(skip_serializing_if="String::is_empty")]
#[serde(with = "proxmox::tools::serde::string_as_base64")]
pub password: String,
--
2.20.1
next prev parent reply other threads:[~2020-10-28 11:37 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-10-28 11:36 [pbs-devel] [PATCH proxmox-backup 00/16] API tokens Fabian Grünbichler
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-widget-toolkit] add PermissionView Fabian Grünbichler
2020-10-28 16:18 ` [pbs-devel] applied: " Thomas Lamprecht
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-backup 01/16] api: add Authid as wrapper around Userid Fabian Grünbichler
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox] rpcenv: rename user to auth_id Fabian Grünbichler
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-backup 02/16] config: add token.shadow file Fabian Grünbichler
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-backup 03/16] replace Userid with Authid Fabian Grünbichler
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-backup 04/16] REST: extract and handle API tokens Fabian Grünbichler
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-backup 05/16] api: add API token endpoints Fabian Grünbichler
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-backup 06/16] api: allow listing users + tokens Fabian Grünbichler
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-backup 07/16] api: add permissions endpoint Fabian Grünbichler
2020-10-28 11:36 ` Fabian Grünbichler [this message]
2020-10-28 11:36 ` [pbs-devel] [PATCH proxmox-backup 09/16] owner checks: handle backups owned by API tokens Fabian Grünbichler
2020-10-28 11:37 ` [pbs-devel] [PATCH proxmox-backup 10/16] tasks: allow unpriv users to read their tokens' tasks Fabian Grünbichler
2020-10-28 11:37 ` [pbs-devel] [PATCH proxmox-backup 11/16] manager: add token commands Fabian Grünbichler
2020-10-28 11:37 ` [pbs-devel] [PATCH proxmox-backup 12/16] manager: add user permissions command Fabian Grünbichler
2020-10-28 11:37 ` [pbs-devel] [PATCH proxmox-backup 13/16] gui: add permissions button to user view Fabian Grünbichler
2020-10-28 11:37 ` [pbs-devel] [PATCH proxmox-backup 14/16] gui: add API token UI Fabian Grünbichler
2020-10-28 11:37 ` [pbs-devel] [PATCH proxmox-backup 15/16] acls: allow viewing/editing user's token ACLs Fabian Grünbichler
2020-10-28 11:37 ` [pbs-devel] [PATCH proxmox-backup 16/16] gui: add API " Fabian Grünbichler
2020-10-29 14:23 ` [pbs-devel] applied: [PATCH proxmox-backup 00/16] API tokens Wolfgang Bumiller
2020-10-29 19:50 ` [pbs-devel] " Thomas Lamprecht
2020-10-30 8:03 ` Fabian Grünbichler
2020-10-30 8:48 ` Thomas Lamprecht
2020-10-30 9:55 ` Fabian Grünbichler
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20201028113632.814586-11-f.gruenbichler@proxmox.com \
--to=f.gruenbichler@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.