use crate::wallet::encryption::EncryptedSecretKey;
use crate::wallet::{Error, Result};
use chrono::{DateTime, Duration, Utc};
use secrecy::{ExposeSecret, Secret};
use std::path::PathBuf;
const PASSWORD_EXPIRATION_TIME_SECS: i64 = 120;
pub struct AuthenticationManager {
password: Option<Secret<String>>,
password_expires_at: Option<DateTime<Utc>>,
wallet_dir: PathBuf,
}
impl AuthenticationManager {
pub fn new(wallet_dir: PathBuf) -> Self {
Self {
password: None,
password_expires_at: None,
wallet_dir,
}
}
pub fn authenticate_with_password(&mut self, password: String) -> Result<()> {
self.verify_password(&password)?;
self.password = Some(Secret::new(password));
self.reset_password_expiration_time();
Ok(())
}
fn verify_password(&self, password: &str) -> Result<()> {
let encrypted_secret_key = EncryptedSecretKey::from_file(self.wallet_dir.as_path())?;
encrypted_secret_key.decrypt(password)?;
Ok(())
}
fn reset_password_expiration_time(&mut self) {
self.password_expires_at =
Some(Utc::now() + Duration::seconds(PASSWORD_EXPIRATION_TIME_SECS));
}
pub fn authenticate(&mut self) -> Result<Option<String>> {
if EncryptedSecretKey::file_exists(self.wallet_dir.as_path()) {
if let (Some(password), Some(expiration_time)) =
(&self.password.to_owned(), self.password_expires_at)
{
let password = password.expose_secret().to_owned();
if self.verify_password(&password).is_err() {
self.password = None;
return Err(Error::WalletPasswordIncorrect);
}
if Utc::now() <= expiration_time {
self.reset_password_expiration_time();
Ok(Some(password))
} else {
self.password = None;
Err(Error::WalletPasswordExpired)
}
} else {
Err(Error::WalletPasswordRequired)
}
} else {
Ok(None)
}
}
}