Skip to content

Commit

Permalink
Add tools authenticator command.
Browse files Browse the repository at this point in the history
Supports export and import of unencrypted TOTP secrets.
  • Loading branch information
tmpfs committed Jul 14, 2024
1 parent 163f559 commit cd0b3db
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
113 changes: 113 additions & 0 deletions crates/sos/src/commands/tools/authenticator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use crate::{
helpers::{
account::resolve_user, messages::success, readline::read_flag,
},
Error, Result,
};
use clap::Subcommand;
use sos_net::sdk::prelude::{
export_authenticator, import_authenticator, Account, AccountRef,
FolderCreate, NewFolderOptions, VaultFlags,
};
use std::path::PathBuf;

#[derive(Subcommand, Debug)]
pub enum Command {
/// Export the TOTP secrets in an authenticator folder
Export {
/// Account name or address
#[clap(short, long)]
account: Option<AccountRef>,

/// Include PNG images of the QR codes in the zip archive
#[clap(short, long)]
qr_codes: bool,

/// Output zip archive
file: PathBuf,
},
/// Import the TOTP secrets from a zip archive
Import {
/// Account name or address
#[clap(short, long)]
account: Option<AccountRef>,

/// Name used when creating a new authenticator folder
#[clap(short, long)]
folder_name: Option<String>,

/// Input zip archive
file: PathBuf,
},
}

pub async fn run(cmd: Command) -> Result<()> {
match cmd {
Command::Export {
account,
file,
qr_codes,
} => {
let user = resolve_user(account.as_ref(), false).await?;
let owner = user.write().await;
let authenticator = owner
.authenticator_folder()
.await
.ok_or(Error::NoAuthenticatorFolder)?;

let storage = owner.storage().await?;
let storage = storage.read().await;
let folder = storage.cache().get(authenticator.id()).unwrap();

export_authenticator(file, folder.keeper(), qr_codes).await?;
success("authenticator TOTP secrets exported");
}
Command::Import {
account,
file,
folder_name,
} => {
let user = resolve_user(account.as_ref(), false).await?;
let mut owner = user.write().await;

let folder = if let Some(authenticator) =
owner.authenticator_folder().await
{
let prompt = format!(
r#"Overwrite secrets in the "{}" folder (y/n)? "#,
authenticator.name()
);

if read_flag(Some(&prompt))? {
Some(authenticator)
} else {
None
}
} else {
let options = NewFolderOptions {
flags: VaultFlags::AUTHENTICATOR
| VaultFlags::LOCAL
| VaultFlags::NO_SYNC,
..Default::default()
};
let FolderCreate { folder, .. } = owner
.create_folder(
folder_name.unwrap_or("Authenticator".to_string()),
options,
)
.await?;
Some(folder)
};

if let Some(folder) = folder {
let storage = owner.storage().await?;
let mut storage = storage.write().await;
let folder =
storage.cache_mut().get_mut(folder.id()).unwrap();
import_authenticator(file, folder.keeper_mut()).await?;
success("authenticator TOTP secrets imported");
}
}
}
Ok(())
}
9 changes: 9 additions & 0 deletions crates/sos/src/commands/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ use std::path::PathBuf;
use terminal_banner::{Banner, Padding};

mod audit;
mod authenticator;
mod check;
mod events;
mod security_report;

use audit::Command as AuditCommand;
use authenticator::Command as AuthenticatorCommand;
use check::{verify_events, Command as CheckCommand};
use events::Command as EventsCommand;
use security_report::SecurityReportFormat;
Expand All @@ -38,6 +40,12 @@ pub enum Command {
#[clap(subcommand)]
cmd: AuditCommand,
},
/// Export and import TOTP secrets.
#[clap(alias = "auth")]
Authenticator {
#[clap(subcommand)]
cmd: AuthenticatorCommand,
},
/// Check file status and integrity.
Check {
#[clap(subcommand)]
Expand Down Expand Up @@ -105,6 +113,7 @@ pub enum Command {
pub async fn run(cmd: Command) -> Result<()> {
match cmd {
Command::Audit { cmd } => audit::run(cmd).await?,
Command::Authenticator { cmd } => authenticator::run(cmd).await?,
Command::Check { cmd } => check::run(cmd).await?,
Command::ConvertCipher {
account,
Expand Down
4 changes: 4 additions & 0 deletions crates/sos/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ pub enum Error {
#[error(r#"initial sync has errors: {0}"#)]
InitialSync(SyncError<sos_net::Error>),

/// Could not find an authenticator folder.
#[error("could not find an authenticator folder")]
NoAuthenticatorFolder,

/// Sync failed.
#[error(r#"sync failed"#)]
SyncFail,
Expand Down

0 comments on commit cd0b3db

Please sign in to comment.