client: rustfmt

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht
2022-04-14 14:08:48 +02:00
parent f9a5beaa15
commit bdfa637058
17 changed files with 722 additions and 529 deletions

View File

@ -1,14 +1,14 @@
use std::convert::TryFrom;
use std::path::PathBuf;
use std::os::unix::io::{FromRawFd, RawFd};
use std::io::Read;
use std::os::unix::io::{FromRawFd, RawFd};
use std::path::PathBuf;
use anyhow::{bail, format_err, Error};
use serde_json::Value;
use proxmox_sys::linux::tty;
use proxmox_sys::fs::file_get_contents;
use proxmox_schema::*;
use proxmox_sys::fs::file_get_contents;
use proxmox_sys::linux::tty;
use pbs_api_types::CryptMode;
@ -102,11 +102,12 @@ fn do_crypto_parameters(param: &Value, keep_keyfd_open: bool) -> Result<CryptoPa
let key_fd = match param.get("keyfd") {
Some(Value::Number(key_fd)) => Some(
RawFd::try_from(key_fd
.as_i64()
.ok_or_else(|| format_err!("bad key fd: {:?}", key_fd))?
RawFd::try_from(
key_fd
.as_i64()
.ok_or_else(|| format_err!("bad key fd: {:?}", key_fd))?,
)
.map_err(|err| format_err!("bad key fd: {:?}: {}", key_fd, err))?
.map_err(|err| format_err!("bad key fd: {:?}: {}", key_fd, err))?,
),
Some(_) => bail!("bad --keyfd parameter type"),
None => None,
@ -120,11 +121,12 @@ fn do_crypto_parameters(param: &Value, keep_keyfd_open: bool) -> Result<CryptoPa
let master_pubkey_fd = match param.get("master-pubkey-fd") {
Some(Value::Number(key_fd)) => Some(
RawFd::try_from(key_fd
.as_i64()
.ok_or_else(|| format_err!("bad master public key fd: {:?}", key_fd))?
RawFd::try_from(
key_fd
.as_i64()
.ok_or_else(|| format_err!("bad master public key fd: {:?}", key_fd))?,
)
.map_err(|err| format_err!("bad public master key fd: {:?}: {}", key_fd, err))?
.map_err(|err| format_err!("bad public master key fd: {:?}: {}", key_fd, err))?,
),
Some(_) => bail!("bad --master-pubkey-fd parameter type"),
None => None,
@ -151,7 +153,9 @@ fn do_crypto_parameters(param: &Value, keep_keyfd_open: bool) -> Result<CryptoPa
if keep_keyfd_open {
// don't close fd if requested, and try to reset seek position
std::mem::forget(input);
unsafe { libc::lseek(fd, 0, libc::SEEK_SET); }
unsafe {
libc::lseek(fd, 0, libc::SEEK_SET);
}
}
Some(KeyWithSource::from_fd(data))
}
@ -373,14 +377,14 @@ fn create_testdir(name: &str) -> Result<String, Error> {
// WARNING: there must only be one test for crypto_parameters as the default key handling is not
// safe w.r.t. concurrency
fn test_crypto_parameters_handling() -> Result<(), Error> {
use serde_json::json;
use proxmox_sys::fs::{replace_file, CreateOptions};
use serde_json::json;
let some_key = vec![1;1];
let default_key = vec![2;1];
let some_key = vec![1; 1];
let default_key = vec![2; 1];
let some_master_key = vec![3;1];
let default_master_key = vec![4;1];
let some_master_key = vec![3; 1];
let default_master_key = vec![4; 1];
let testdir = create_testdir("key_source")?;
@ -441,14 +445,19 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
};
replace_file(&keypath, &some_key, CreateOptions::default(), false)?;
replace_file(&master_keypath, &some_master_key, CreateOptions::default(), false)?;
replace_file(
&master_keypath,
&some_master_key,
CreateOptions::default(),
false,
)?;
// no params, no default key == no key
let res = crypto_parameters(&json!({}));
assert_eq!(res.unwrap(), no_key_res);
// keyfile param == key from keyfile
let res = crypto_parameters(&json!({"keyfile": keypath}));
let res = crypto_parameters(&json!({ "keyfile": keypath }));
assert_eq!(res.unwrap(), some_key_res);
// crypt mode none == no key
@ -469,13 +478,19 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
assert_eq!(res.unwrap(), some_key_res);
// invalid keyfile parameter always errors
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err());
assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err());
assert!(
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()
);
assert!(
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()
);
// now set a default key
unsafe { set_test_encryption_key(Ok(Some(default_key))); }
unsafe {
set_test_encryption_key(Ok(Some(default_key)));
}
// and repeat
@ -484,7 +499,7 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
assert_eq!(res.unwrap(), default_key_res);
// keyfile param == key from keyfile
let res = crypto_parameters(&json!({"keyfile": keypath}));
let res = crypto_parameters(&json!({ "keyfile": keypath }));
assert_eq!(res.unwrap(), some_key_res);
// crypt mode none == no key
@ -507,13 +522,19 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
assert_eq!(res.unwrap(), some_key_res);
// invalid keyfile parameter always errors
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err());
assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err());
assert!(
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()
);
assert!(
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()
);
// now make default key retrieval error
unsafe { set_test_encryption_key(Err(format_err!("test error"))); }
unsafe {
set_test_encryption_key(Err(format_err!("test error")));
}
// and repeat
@ -521,7 +542,7 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
assert!(crypto_parameters(&json!({})).is_err());
// keyfile param == key from keyfile
let res = crypto_parameters(&json!({"keyfile": keypath}));
let res = crypto_parameters(&json!({ "keyfile": keypath }));
assert_eq!(res.unwrap(), some_key_res);
// crypt mode none == no key
@ -542,18 +563,26 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
assert_eq!(res.unwrap(), some_key_res);
// invalid keyfile parameter always errors
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err());
assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err());
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err());
assert!(
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()
);
assert!(
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()
);
// now remove default key again
unsafe { set_test_encryption_key(Ok(None)); }
unsafe {
set_test_encryption_key(Ok(None));
}
// set a default master key
unsafe { set_test_default_master_pubkey(Ok(Some(default_master_key))); }
unsafe {
set_test_default_master_pubkey(Ok(Some(default_master_key)));
}
// and use an explicit master key
assert!(crypto_parameters(&json!({"master-pubkey-file": master_keypath})).is_err());
assert!(crypto_parameters(&json!({ "master-pubkey-file": master_keypath })).is_err());
// just a default == no key
let res = crypto_parameters(&json!({}));
assert_eq!(res.unwrap(), no_key_res);
@ -562,35 +591,55 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
let res = crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": master_keypath}));
assert_eq!(res.unwrap(), some_key_some_master_res);
// same with fallback to default master key
let res = crypto_parameters(&json!({"keyfile": keypath}));
let res = crypto_parameters(&json!({ "keyfile": keypath }));
assert_eq!(res.unwrap(), some_key_default_master_res);
// crypt mode none == error
assert!(crypto_parameters(&json!({"crypt-mode": "none", "master-pubkey-file": master_keypath})).is_err());
assert!(crypto_parameters(
&json!({"crypt-mode": "none", "master-pubkey-file": master_keypath})
)
.is_err());
// with just default master key == no key
let res = crypto_parameters(&json!({"crypt-mode": "none"}));
assert_eq!(res.unwrap(), no_key_res);
// crypt mode encrypt without enc key == error
assert!(crypto_parameters(&json!({"crypt-mode": "encrypt", "master-pubkey-file": master_keypath})).is_err());
assert!(crypto_parameters(
&json!({"crypt-mode": "encrypt", "master-pubkey-file": master_keypath})
)
.is_err());
assert!(crypto_parameters(&json!({"crypt-mode": "encrypt"})).is_err());
// crypt mode none with explicit key == Error
assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath, "master-pubkey-file": master_keypath})).is_err());
assert!(crypto_parameters(
&json!({"crypt-mode": "none", "keyfile": keypath, "master-pubkey-file": master_keypath})
)
.is_err());
assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err());
// crypt mode encrypt with keyfile == key from keyfile with correct mode
let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath, "master-pubkey-file": master_keypath}));
let res = crypto_parameters(
&json!({"crypt-mode": "encrypt", "keyfile": keypath, "master-pubkey-file": master_keypath}),
);
assert_eq!(res.unwrap(), some_key_some_master_res);
let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath}));
assert_eq!(res.unwrap(), some_key_default_master_res);
// invalid master keyfile parameter always errors when a key is passed, even with a valid
// default master key
assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath})).is_err());
assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "none"})).is_err());
assert!(
crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath}))
.is_err()
);
assert!(crypto_parameters(
&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "none"})
)
.is_err());
assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "sign-only"})).is_err());
assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "encrypt"})).is_err());
assert!(crypto_parameters(
&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "encrypt"})
)
.is_err());
Ok(())
}

View File

@ -1,20 +1,20 @@
//! Shared tools useful for common CLI clients.
use std::collections::HashMap;
use std::env::VarError::{NotPresent, NotUnicode};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::os::unix::io::FromRawFd;
use std::env::VarError::{NotUnicode, NotPresent};
use std::io::{BufReader, BufRead};
use std::process::Command;
use anyhow::{bail, format_err, Context, Error};
use serde_json::{json, Value};
use xdg::BaseDirectories;
use proxmox_schema::*;
use proxmox_router::cli::{complete_file_name, shellword_split};
use proxmox_schema::*;
use proxmox_sys::fs::file_get_json;
use pbs_api_types::{BACKUP_REPO_URL, Authid, RateLimitConfig, UserWithTokens};
use pbs_api_types::{Authid, RateLimitConfig, UserWithTokens, BACKUP_REPO_URL};
use pbs_datastore::BackupDir;
use pbs_tools::json::json_object_to_query;
@ -48,7 +48,6 @@ pub const CHUNK_SIZE_SCHEMA: Schema = IntegerSchema::new("Chunk size in KB. Must
///
/// Only return the first line of data (without CRLF).
pub fn get_secret_from_env(base_name: &str) -> Result<Option<String>, Error> {
let firstline = |data: String| -> String {
match data.lines().next() {
Some(line) => line.to_string(),
@ -68,19 +67,24 @@ pub fn get_secret_from_env(base_name: &str) -> Result<Option<String>, Error> {
match std::env::var(base_name) {
Ok(p) => return Ok(Some(firstline(p))),
Err(NotUnicode(_)) => bail!(format!("{} contains bad characters", base_name)),
Err(NotPresent) => {},
Err(NotPresent) => {}
};
let env_name = format!("{}_FD", base_name);
match std::env::var(&env_name) {
Ok(fd_str) => {
let fd: i32 = fd_str.parse()
.map_err(|err| format_err!("unable to parse file descriptor in ENV({}): {}", env_name, err))?;
let fd: i32 = fd_str.parse().map_err(|err| {
format_err!(
"unable to parse file descriptor in ENV({}): {}",
env_name,
err
)
})?;
let mut file = unsafe { File::from_raw_fd(fd) };
return Ok(Some(firstline_file(&mut file)?));
}
Err(NotUnicode(_)) => bail!(format!("{} contains bad characters", env_name)),
Err(NotPresent) => {},
Err(NotPresent) => {}
}
let env_name = format!("{}_FILE", base_name);
@ -91,7 +95,7 @@ pub fn get_secret_from_env(base_name: &str) -> Result<Option<String>, Error> {
return Ok(Some(firstline_file(&mut file)?));
}
Err(NotUnicode(_)) => bail!(format!("{} contains bad characters", env_name)),
Err(NotPresent) => {},
Err(NotPresent) => {}
}
let env_name = format!("{}_CMD", base_name);
@ -104,7 +108,7 @@ pub fn get_secret_from_env(base_name: &str) -> Result<Option<String>, Error> {
return Ok(Some(firstline(output)));
}
Err(NotUnicode(_)) => bail!(format!("{} contains bad characters", env_name)),
Err(NotPresent) => {},
Err(NotPresent) => {}
}
Ok(None)
@ -157,21 +161,18 @@ fn connect_do(
let fingerprint = std::env::var(ENV_VAR_PBS_FINGERPRINT).ok();
let password = get_secret_from_env(ENV_VAR_PBS_PASSWORD)?;
let options = HttpClientOptions::new_interactive(password, fingerprint)
.rate_limit(rate_limit);
let options = HttpClientOptions::new_interactive(password, fingerprint).rate_limit(rate_limit);
HttpClient::new(server, port, auth_id, options)
}
/// like get, but simply ignore errors and return Null instead
pub async fn try_get(repo: &BackupRepository, url: &str) -> Value {
let fingerprint = std::env::var(ENV_VAR_PBS_FINGERPRINT).ok();
let password = get_secret_from_env(ENV_VAR_PBS_PASSWORD).unwrap_or(None);
// ticket cache, but no questions asked
let options = HttpClientOptions::new_interactive(password, fingerprint)
.interactive(false);
let options = HttpClientOptions::new_interactive(password, fingerprint).interactive(false);
let client = match HttpClient::new(repo.host(), repo.port(), repo.auth_id(), options) {
Ok(v) => v,
@ -196,7 +197,6 @@ pub fn complete_backup_group(_arg: &str, param: &HashMap<String, String>) -> Vec
}
pub async fn complete_backup_group_do(param: &HashMap<String, String>) -> Vec<String> {
let mut result = vec![];
let repo = match extract_repository_from_map(param) {
@ -225,8 +225,10 @@ pub fn complete_group_or_snapshot(arg: &str, param: &HashMap<String, String>) ->
proxmox_async::runtime::main(async { complete_group_or_snapshot_do(arg, param).await })
}
pub async fn complete_group_or_snapshot_do(arg: &str, param: &HashMap<String, String>) -> Vec<String> {
pub async fn complete_group_or_snapshot_do(
arg: &str,
param: &HashMap<String, String>,
) -> Vec<String> {
if arg.matches('/').count() < 2 {
let groups = complete_backup_group_do(param).await;
let mut result = vec![];
@ -245,7 +247,6 @@ pub fn complete_backup_snapshot(_arg: &str, param: &HashMap<String, String>) ->
}
pub async fn complete_backup_snapshot_do(param: &HashMap<String, String>) -> Vec<String> {
let mut result = vec![];
let repo = match extract_repository_from_map(param) {
@ -259,9 +260,11 @@ pub async fn complete_backup_snapshot_do(param: &HashMap<String, String>) -> Vec
if let Some(list) = data.as_array() {
for item in list {
if let (Some(backup_id), Some(backup_type), Some(backup_time)) =
(item["backup-id"].as_str(), item["backup-type"].as_str(), item["backup-time"].as_i64())
{
if let (Some(backup_id), Some(backup_type), Some(backup_time)) = (
item["backup-id"].as_str(),
item["backup-type"].as_str(),
item["backup-time"].as_i64(),
) {
if let Ok(snapshot) = BackupDir::new(backup_type, backup_id, backup_time) {
result.push(snapshot.relative_path().to_str().unwrap().to_owned());
}
@ -277,7 +280,6 @@ pub fn complete_server_file_name(_arg: &str, param: &HashMap<String, String>) ->
}
pub async fn complete_server_file_name_do(param: &HashMap<String, String>) -> Vec<String> {
let mut result = vec![];
let repo = match extract_repository_from_map(param) {
@ -286,12 +288,10 @@ pub async fn complete_server_file_name_do(param: &HashMap<String, String>) -> Ve
};
let snapshot: BackupDir = match param.get("snapshot") {
Some(path) => {
match path.parse() {
Ok(v) => v,
_ => return result,
}
}
Some(path) => match path.parse() {
Ok(v) => v,
_ => return result,
},
_ => return result,
};
@ -299,7 +299,8 @@ pub async fn complete_server_file_name_do(param: &HashMap<String, String>) -> Ve
"backup-type": snapshot.group().backup_type(),
"backup-id": snapshot.group().backup_id(),
"backup-time": snapshot.backup_time(),
})).unwrap();
}))
.unwrap();
let path = format!("api2/json/admin/datastore/{}/files?{}", repo.store(), query);
@ -350,14 +351,15 @@ pub fn complete_img_archive_name(arg: &str, param: &HashMap<String, String>) ->
}
pub fn complete_chunk_size(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
let mut result = vec![];
let mut size = 64;
loop {
result.push(size.to_string());
size *= 2;
if size > 4096 { break; }
if size > 4096 {
break;
}
}
result
@ -368,7 +370,6 @@ pub fn complete_auth_id(_arg: &str, param: &HashMap<String, String>) -> Vec<Stri
}
pub async fn complete_auth_id_do(param: &HashMap<String, String>) -> Vec<String> {
let mut result = vec![];
let repo = match extract_repository_from_map(param) {