#![allow(unused_imports)]
use ate::prelude::*;
use chrono::Duration;
use error_chain::bail;
use std::io::stdout;
use std::io::Write;
use std::sync::Arc;
use tracing::{debug, error, info, instrument, span, trace, warn, Level};
use url::Url;
use crate::error::*;
use crate::helper::*;
use crate::opt::*;
use crate::prelude::*;
use crate::request::*;
pub async fn login_command(
registry: &Registry,
username: String,
password: String,
verification_code: Option<String>,
auth: Url,
print_message_of_the_day: bool,
) -> Result<AteSessionUser, LoginError> {
let chain = registry.open_cmd(&auth).await?;
let prefix = format!("remote-login:{}:", username);
let read_key = password_to_read_key(&prefix, &password, 15, KeySize::Bit192);
let login = LoginRequest {
email: username.clone(),
secret: read_key,
verification_code,
};
trace!("invoking login (email={})", login.email);
let response: Result<LoginResponse, LoginFailed> = chain.invoke(login).await?;
let result = response?;
if print_message_of_the_day {
if let Some(message_of_the_day) = result.message_of_the_day {
if is_tty_stderr() {
eprintln!("{}", message_of_the_day);
}
}
}
Ok(result.authority)
}
pub(crate) async fn main_session_start(
token_string: Option<String>,
token_file_path: Option<String>,
auth_url: Option<url::Url>,
) -> Result<AteSessionType, LoginError> {
let mut session = None;
if session.is_none() {
if let Some(path) = token_file_path {
if token_string.is_some() {
eprintln!("You must not provide both a token string and a token file path - only specify one of them!");
std::process::exit(1);
}
let path = shellexpand::tilde(path.as_str()).to_string();
#[cfg(feature = "enable_full")]
if let Ok(token) = tokio::fs::read_to_string(path).await {
session = Some(b64_to_session(token));
}
#[cfg(not(feature = "enable_full"))]
if let Ok(token) = std::fs::read_to_string(path) {
session = Some(b64_to_session(token));
}
}
}
if session.is_none() {
if let Some(token) = token_string {
session = Some(b64_to_session(token));
}
}
let session = match session {
Some(a) => a,
None => {
if let Some(auth) = auth_url.clone() {
AteSessionType::User(main_login(None, None, auth).await?)
} else {
AteSessionType::User(AteSessionUser::default())
}
}
};
Ok(session)
}
pub async fn main_session_prompt(auth_url: url::Url) -> Result<AteSessionUser, LoginError> {
main_session_user(None, None, Some(auth_url)).await
}
pub async fn main_session_user(
token_string: Option<String>,
token_file_path: Option<String>,
auth_url: Option<url::Url>,
) -> Result<AteSessionUser, LoginError> {
let session = main_session_start(token_string, token_file_path, auth_url.clone()).await?;
let session = match session {
AteSessionType::Group(a) => a.inner,
AteSessionType::User(a) => AteSessionInner::User(a),
AteSessionType::Sudo(a) => AteSessionInner::Sudo(a),
AteSessionType::Nothing => AteSessionInner::Nothing,
};
Ok(match session {
AteSessionInner::User(a) => a,
AteSessionInner::Sudo(a) => a.inner,
AteSessionInner::Nothing => AteSessionUser::new()
})
}
pub async fn main_user_details(session: AteSessionUser) -> Result<(), LoginError> {
println!("# User Details");
println!("");
println!("Name: {}", session.identity);
if let Some(uid) = session.user.uid() {
println!("UID: {}", uid);
}
Ok(())
}
pub async fn main_login(
username: Option<String>,
password: Option<String>,
auth: Url,
) -> Result<AteSessionUser, LoginError> {
let username = match username {
Some(a) => a,
None => {
if !is_tty_stdin() {
bail!(LoginErrorKind::InvalidArguments);
}
eprint!("Username: ");
stdout().lock().flush()?;
let mut s = String::new();
std::io::stdin()
.read_line(&mut s)
.expect("Did not enter a valid username");
s.trim().to_string()
}
};
let password = match password {
Some(a) => a,
None => {
if !is_tty_stdin() {
bail!(LoginErrorKind::InvalidArguments);
}
let pass = rpassword_wasi::prompt_password("Password: ").unwrap();
pass.trim().to_string()
}
};
let registry = ate::mesh::Registry::new(&conf_cmd()).await.cement();
let response = login_command(
®istry,
username.clone(),
password.clone(),
None,
auth.clone(),
true,
)
.await;
let ret = handle_login_response(®istry, response, username, password, auth).await?;
Ok(ret)
}
pub(crate) async fn handle_login_response(
registry: &Registry,
mut response: Result<AteSessionUser, LoginError>,
username: String,
password: String,
auth: Url,
) -> Result<AteSessionUser, LoginError> {
let mut was_unverified = false;
if let Err(LoginError(LoginErrorKind::Unverified(_), _)) = &response {
was_unverified = true;
if !is_tty_stdin() {
bail!(LoginErrorKind::InvalidArguments);
}
eprintln!("Check your email for a verification code and enter it below");
eprint!("Verification Code: ");
stdout().lock().flush()?;
let mut s = String::new();
std::io::stdin()
.read_line(&mut s)
.expect("Did not enter a valid code");
let verification_code = s.trim().to_string();
response = login_command(
registry,
username,
password,
Some(verification_code),
auth,
true,
)
.await;
}
match response {
Ok(a) => Ok(a),
Err(LoginError(LoginErrorKind::AccountLocked(duration), _)) => {
if duration > Duration::days(1).to_std().unwrap() {
eprintln!(
"This account has been locked for {} days",
(duration.as_secs() as u64 / 86400u64)
);
} else if duration > Duration::hours(1).to_std().unwrap() {
eprintln!(
"This account has been locked for {} hours",
(duration.as_secs() as u64 / 3600u64)
);
} else if duration > Duration::minutes(1).to_std().unwrap() {
eprintln!(
"This account has been locked for {} minutes",
(duration.as_secs() as u64 / 60u64)
);
} else {
eprintln!(
"This account has been locked for {} seconds",
(duration.as_secs() as u64)
);
}
std::process::exit(1);
}
Err(LoginError(LoginErrorKind::WrongPassword, _)) => {
if was_unverified {
eprintln!("Either the password or verification code was incorrect");
} else {
eprintln!("The password was incorrect");
}
eprintln!("(Warning! Repeated failed attempts will trigger a short ban)");
std::process::exit(1);
}
Err(LoginError(LoginErrorKind::NotFound(username), _)) => {
eprintln!("Account does not exist ({})", username);
std::process::exit(1);
}
Err(LoginError(LoginErrorKind::Unverified(username), _)) => {
eprintln!(
"The account ({}) has not yet been verified - please check your email.",
username
);
std::process::exit(1);
}
Err(err) => {
bail!(err);
}
}
}