fix #4734: manager: add user tfa {list, delete} commands

Adds the commands

    proxmox-backup-manager user tfa list <userid>
    proxmox-backup-manager user tfa delete <userid> <id>

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
This commit is contained in:
Maximiliano Sandoval 2023-06-23 10:28:39 +02:00 committed by Wolfgang Bumiller
parent 223842887f
commit 9c85840fe2
3 changed files with 83 additions and 2 deletions

View File

@ -69,7 +69,7 @@ async fn tfa_update_auth(
},
)]
/// Add a TOTP secret to the user.
fn list_user_tfa(userid: Userid) -> Result<Vec<methods::TypedTfaInfo>, Error> {
pub fn list_user_tfa(userid: Userid) -> Result<Vec<methods::TypedTfaInfo>, Error> {
let _lock = crate::config::tfa::read_lock()?;
methods::list_user_tfa(&crate::config::tfa::read()?, userid.as_str())
@ -122,7 +122,7 @@ fn get_tfa_entry(userid: Userid, id: String) -> Result<methods::TypedTfaInfo, Er
},
)]
/// Delete a single TFA entry.
async fn delete_tfa(
pub async fn delete_tfa(
userid: Userid,
id: String,
password: Option<String>,

View File

@ -157,6 +157,40 @@ fn list_permissions(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Val
Ok(Value::Null)
}
#[api(
input: {
properties: {
"output-format": {
schema: OUTPUT_FORMAT,
optional: true,
},
userid: {
type: Userid,
}
},
}
)]
/// List all tfa methods for a user.
fn list_user_tfa(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
let output_format = get_output_format(&param);
let info = &api2::access::tfa::API_METHOD_LIST_USER_TFA;
let mut data = match info.handler {
ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
_ => unreachable!(),
};
let options = default_table_format_options()
.column(ColumnConfig::new("id"))
.column(ColumnConfig::new("type"))
.column(ColumnConfig::new("description"))
.column(ColumnConfig::new("created").renderer(pbs_tools::format::render_epoch));
format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
Ok(Value::Null)
}
pub fn user_commands() -> CommandLineInterface {
let cmd_def = CliCommandMap::new()
.insert("list", CliCommand::new(&API_METHOD_LIST_USERS))
@ -196,6 +230,7 @@ pub fn user_commands() -> CommandLineInterface {
.completion_cb("userid", pbs_config::user::complete_userid)
.completion_cb("token-name", pbs_config::user::complete_token_name),
)
.insert("tfa", tfa_commands())
.insert(
"permissions",
CliCommand::new(&API_METHOD_LIST_PERMISSIONS)
@ -206,3 +241,21 @@ pub fn user_commands() -> CommandLineInterface {
cmd_def.into()
}
fn tfa_commands() -> CommandLineInterface {
CliCommandMap::new()
.insert(
"list",
CliCommand::new(&API_METHOD_LIST_USER_TFA)
.arg_param(&["userid"])
.completion_cb("userid", pbs_config::user::complete_userid),
)
.insert(
"delete",
CliCommand::new(&api2::access::tfa::API_METHOD_DELETE_TFA)
.arg_param(&["userid", "id"])
.completion_cb("userid", pbs_config::user::complete_userid)
.completion_cb("id", proxmox_backup::config::tfa::complete_tfa_id),
)
.into()
}

View File

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom};
use std::os::unix::fs::OpenOptionsExt;
@ -302,3 +303,30 @@ impl proxmox_tfa::api::UserChallengeAccess for TfaUserChallengeData {
TfaUserChallengeData::save(self)
}
}
// shell completion helper
pub fn complete_tfa_id(_arg: &str, param: &HashMap<String, String>) -> Vec<String> {
let mut results = Vec::new();
let data = match read() {
Ok(data) => data,
Err(_err) => return results,
};
let user = match param
.get("userid")
.and_then(|user_name| data.users.get(user_name))
{
Some(user) => user,
None => return results,
};
results.extend(user.totp.iter().map(|token| token.info.id.clone()));
results.extend(user.u2f.iter().map(|token| token.info.id.clone()));
results.extend(user.webauthn.iter().map(|token| token.info.id.clone()));
results.extend(user.yubico.iter().map(|token| token.info.id.clone()));
if user.recovery.is_some() {
results.push("recovery".to_string());
};
results
}