prompting for username when multiple tokens exist on logout (#559)

This commit is contained in:
James Hodgkinson 2021-08-08 09:56:09 +10:00 committed by GitHub
parent eb4b25719b
commit 100ef49e4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 27 deletions

View file

@ -80,28 +80,13 @@ impl CommonOpt {
} else {
// Unable to automatically select the user because multiple tokens exist
// so we'll prompt the user to select one
let mut options = Vec::new();
for option in tokens.iter() {
options.push(String::from(option.0));
}
let user_select = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Multiple authentication tokens exist. Please select one")
.default(0)
.items(&options)
.interact();
let selection = match user_select {
Err(error) => {
eprintln!("Failed to handle user input: {:?}", error);
match prompt_for_username_get_token() {
Ok(value) => value,
Err(msg) => {
eprintln!("{}", msg);
std::process::exit(1);
}
Ok(value) => value,
};
debug!("Index of the chosen menu item: {:?}", selection);
let (f_uname, f_token) =
tokens.iter().nth(selection).expect("Memory Corruption");
info!("Using cached token for name {}", f_uname);
f_token.clone()
}
}
}
};
@ -129,3 +114,73 @@ impl CommonOpt {
client
}
}
/// This parses the token store and prompts the user to select their username, returns the username/token as a tuple of Strings
///
/// Used to reduce duplication in implementing [prompt_for_username_get_username] and [prompt_for_username_get_token]
pub fn prompt_for_username_get_values() -> Result<(String, String), String> {
let tokens = match read_tokens() {
Ok(value) => value,
_ => return Err("Error retrieving authentication token store".to_string()),
};
if tokens.is_empty() {
eprintln!("No tokens in store, quitting!");
std::process::exit(1);
}
let mut options = Vec::new();
for option in tokens.iter() {
options.push(String::from(option.0));
}
let user_select = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Authentication tokens exist. Please select one")
.default(0)
.items(&options)
.interact();
let selection = match user_select {
Err(error) => {
eprintln!("Failed to handle user input: {:?}", error);
std::process::exit(1);
}
Ok(value) => value,
};
debug!("Index of the chosen menu item: {:?}", selection);
match tokens.iter().nth(selection) {
Some(value) => {
let (f_uname, f_token) = value;
info!("Using cached token for name {}", f_uname);
debug!("Cached token: {}", f_token);
Ok((f_uname.to_string(), f_token.to_string()))
}
None => {
eprintln!("Memory corruption trying to read token store, quitting!");
std::process::exit(1);
}
}
}
/// This parses the token store and prompts the user to select their username, returns the username as a String
///
/// Powered by [prompt_for_username_get_values]
pub fn prompt_for_username_get_username() -> Result<String, String> {
match prompt_for_username_get_values() {
Ok(value) => {
let (f_user, _) = value;
Ok(f_user)
}
Err(err) => Err(err),
}
}
/// This parses the token store and prompts the user to select their username, returns the token as a String
///
/// Powered by [prompt_for_username_get_values]
pub fn prompt_for_username_get_token() -> Result<String, String> {
match prompt_for_username_get_values() {
Ok(value) => {
let (_, f_token) = value;
Ok(f_token)
}
Err(err) => Err(err),
}
}

View file

@ -1,4 +1,6 @@
use crate::common::prompt_for_username_get_username;
use crate::{LoginOpt, LogoutOpt, SessionOpt};
use kanidm_client::{ClientError, KanidmClient};
use kanidm_proto::v1::{AuthAllowed, AuthResponse, AuthState, UserAuthToken};
#[cfg(target_family = "unix")]
@ -166,7 +168,9 @@ impl LoginOpt {
let totp = loop {
print!("Enter TOTP: ");
// We flush stdout so it'll write the buffer to screen, continuing operation. Without it, the application halts.
io::stdout().flush().unwrap();
if let Err(e) = io::stdout().flush() {
eprintln!("Somehow we failed to flush stdout: {:?}", e);
};
let mut buffer = String::new();
if let Err(e) = io::stdin().read_line(&mut buffer) {
eprintln!("Failed to read from stdin -> {:?}", e);
@ -202,6 +206,7 @@ impl LoginOpt {
pub fn exec(&self) {
let mut client = self.copt.to_unauth_client();
// TODO: remove this anon, nobody should do default anonymous
let username = self.copt.username.as_deref().unwrap_or("anonymous");
// What auth mechanisms exist?
@ -340,14 +345,26 @@ impl LoginOpt {
impl LogoutOpt {
pub fn debug(&self) -> bool {
self.copt.debug
self.debug
}
pub fn exec(&self) {
let username = self.copt.username.as_deref().unwrap_or("anonymous");
// For now we just remove this from the token store.
// Read the current tokens
let mut _tmp_username = String::new();
let username = match &self.username {
Some(value) => value,
None => {
_tmp_username = match prompt_for_username_get_username() {
Ok(value) => value,
Err(msg) => {
eprintln!("{}", msg);
std::process::exit(1);
}
};
&_tmp_username
}
};
let mut tokens = read_tokens().unwrap_or_else(|_| {
error!("Error retrieving authentication token store");
std::process::exit(1);

View file

@ -284,8 +284,14 @@ pub struct LoginOpt {
#[derive(Debug, StructOpt)]
pub struct LogoutOpt {
#[structopt(flatten)]
pub copt: CommonOpt,
#[structopt(short = "d", long = "debug", env = "KANIDM_DEBUG")]
pub debug: bool,
#[structopt(short = "H", long = "url", env = "KANIDM_URL")]
pub addr: Option<String>,
#[structopt(parse(from_os_str), short = "C", long = "ca", env = "KANIDM_CA_PATH")]
pub ca_path: Option<PathBuf>,
#[structopt()]
pub username: Option<String>,
}
#[derive(Debug, StructOpt)]