#![allow(unused_imports)]
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 ate::error::LoadError;
use ate::error::TransformError;
use ate::prelude::*;
use crate::error::*;
use crate::helper::*;
use crate::helper::*;
use crate::model::*;
use crate::prelude::*;
use crate::request::*;
use crate::service::AuthService;
use super::login::*;
impl AuthService {
pub async fn process_sudo(
self: Arc<Self>,
request: SudoRequest,
) -> Result<SudoResponse, SudoFailed> {
info!("sudo attempt: {}", request.session.identity());
let identity = request.session.identity().to_string();
let token = match &request.session.token {
Some(a) => a,
None => {
warn!("login attempt denied ({}) - no token supplied", identity);
return Err(SudoFailed::MissingToken);
}
};
let master_key = match self.master_key() {
Some(a) => a,
None => {
return Err(SudoFailed::NoMasterKey);
}
};
let super_key = token.unwrap(&master_key)?;
let mut super_session = self.master_session.clone();
super_session.user.add_read_key(&super_key);
let (super_super_key, super_token) = match self.compute_master_key(&super_key) {
Some(a) => a,
None => {
warn!("login attempt denied ({}) - no master key (sudo)", identity);
return Err(SudoFailed::NoMasterKey);
}
};
super_session.user.add_read_key(&super_super_key);
super_session.token = Some(super_token);
let chain_key = chain_key_4hex(identity.as_str(), Some("redo"));
let chain = self.registry.open(&self.auth_url, &chain_key).await?;
let dio = chain.dio_full(&super_session).await;
let user_key = PrimaryKey::from(identity.clone());
let mut user = match dio.load::<User>(&user_key).await {
Ok(a) => a,
Err(err) => {
warn!("login attempt denied ({}) - error - ", err);
bail!(err);
}
};
match user.status.clone() {
UserStatus::Locked(until) => {
let local_now = chrono::Local::now();
let utc_now = local_now.with_timezone(&chrono::Utc);
if until > utc_now {
let duration = until - utc_now;
warn!(
"login attempt denied ({}) - account locked until {}",
identity, until
);
return Err(SudoFailed::AccountLocked(duration.to_std().unwrap()));
}
}
UserStatus::Unverified => {
warn!("login attempt denied ({}) - unverified", identity);
return Err(SudoFailed::Unverified(identity));
}
UserStatus::Nominal => {}
};
let session = {
let mut user = user.as_mut();
if let Some(mut sudo) = match user.sudo.load_mut().await {
Ok(a) => a,
Err(LoadError(LoadErrorKind::NotFound(_), _)) => {
warn!("login attempt denied ({}) - user not found", identity);
return Err(SudoFailed::UserNotFound(identity));
}
Err(err) => {
bail!(err);
}
} {
self.time_keeper.wait_for_high_accuracy().await;
let time = self.time_keeper.current_timestamp_as_duration()?;
let time = time.as_secs() / 30;
let google_auth = google_authenticator::GoogleAuthenticator::new();
if google_auth.verify_code(
sudo.secret.as_str(),
request.authenticator_code.as_str(),
3,
time,
) {
debug!("code authenticated");
} else {
sudo.as_mut().failed_attempts = sudo.failed_attempts + 1;
if sudo.failed_attempts % 5 == 0 {
let ban_time = if sudo.failed_attempts <= 5 {
Duration::seconds(30)
} else if sudo.failed_attempts <= 10 {
Duration::minutes(5)
} else if sudo.failed_attempts <= 15 {
Duration::hours(1)
} else {
Duration::days(1)
};
let local_now = chrono::Local::now();
let utc_now = local_now.with_timezone(&chrono::Utc);
if let Some(utc_ban) = utc_now.checked_add_signed(ban_time) {
user.status = UserStatus::Locked(utc_ban);
}
}
dio.commit().await?;
warn!(
"login attempt denied ({}) - wrong code - attempts={}",
identity, sudo.failed_attempts
);
return Err(SudoFailed::WrongCode);
}
if sudo.failed_attempts > 0 {
sudo.as_mut().failed_attempts = 0;
}
user.status = UserStatus::Nominal;
compute_sudo_auth(&sudo.take(), request.session.clone())
} else {
warn!(
"login attempt denied ({}) - user not found (sudo)",
identity
);
return Err(SudoFailed::UserNotFound(identity));
}
};
dio.commit().await?;
info!("login attempt accepted ({})", identity);
Ok(SudoResponse { authority: session })
}
}