refactor(cli): personal add delegates to shared item_build builders
This commit is contained in:
@@ -162,6 +162,128 @@ pub(crate) fn edit_totp(t: &mut relicario_core::item_types::TotpCore, history: &
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn build_login(
|
||||
title: String, username: Option<String>, url: Option<String>,
|
||||
password: Option<String>, password_stdin: bool, password_prompt: bool,
|
||||
totp_qr: Option<PathBuf>,
|
||||
) -> Result<Item> {
|
||||
use relicario_core::item_types::{LoginCore, TotpAlgorithm, TotpConfig, TotpKind};
|
||||
let parsed_url = match url {
|
||||
Some(s) => Some(url::Url::parse(&s).with_context(|| format!("invalid URL: {s}"))?),
|
||||
None => None,
|
||||
};
|
||||
let password = if let Some(p) = password {
|
||||
Some(Zeroizing::new(p))
|
||||
} else if password_stdin {
|
||||
Some(Zeroizing::new(resolve_secret_line(true, "Password")?))
|
||||
} else if password_prompt {
|
||||
Some(Zeroizing::new(crate::prompt::prompt_secret("Password: ")?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let totp = if let Some(path) = totp_qr {
|
||||
let secret_b32 = crate::helpers::decode_totp_qr(&path)?;
|
||||
let secret_bytes = base32_decode_lenient(&secret_b32)?;
|
||||
Some(TotpConfig {
|
||||
secret: Zeroizing::new(secret_bytes), algorithm: TotpAlgorithm::Sha1,
|
||||
digits: 6, period_seconds: 30, kind: TotpKind::Totp,
|
||||
})
|
||||
} else { None };
|
||||
Ok(Item::new(title, ItemCore::Login(LoginCore { username, password, url: parsed_url, totp })))
|
||||
}
|
||||
|
||||
pub(crate) fn build_secure_note(title: String, body: Option<String>, body_stdin: bool) -> Result<Item> {
|
||||
use relicario_core::item_types::SecureNoteCore;
|
||||
let body = match body {
|
||||
Some(b) => b,
|
||||
None => resolve_secret_multiline(body_stdin, "Enter note body; end with Ctrl-D on a blank line:")?,
|
||||
};
|
||||
Ok(Item::new(title, ItemCore::SecureNote(SecureNoteCore { body: Zeroizing::new(body) })))
|
||||
}
|
||||
|
||||
pub(crate) fn build_identity(
|
||||
title: String, full_name: Option<String>, email: Option<String>,
|
||||
phone: Option<String>, date_of_birth: Option<String>,
|
||||
) -> Result<Item> {
|
||||
use relicario_core::item_types::IdentityCore;
|
||||
let dob = match date_of_birth {
|
||||
Some(s) => Some(chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d")
|
||||
.with_context(|| format!("invalid date {s} (expected YYYY-MM-DD)"))?),
|
||||
None => None,
|
||||
};
|
||||
Ok(Item::new(title, ItemCore::Identity(IdentityCore {
|
||||
full_name, address: None, phone, email, date_of_birth: dob,
|
||||
})))
|
||||
}
|
||||
|
||||
pub(crate) fn build_card(
|
||||
title: String, holder: Option<String>, expiry: Option<String>, kind: &str,
|
||||
number_stdin: bool, cvv_stdin: bool, pin_stdin: bool,
|
||||
) -> Result<Item> {
|
||||
use relicario_core::item_types::CardCore;
|
||||
let number = Zeroizing::new(resolve_secret_line(number_stdin, "Card number")?);
|
||||
let cvv = resolve_secret_line(cvv_stdin, "CVV (blank to skip)")?;
|
||||
let cvv = if cvv.is_empty() { None } else { Some(Zeroizing::new(cvv)) };
|
||||
let pin = resolve_secret_line(pin_stdin, "PIN (blank to skip)")?;
|
||||
let pin = if pin.is_empty() { None } else { Some(Zeroizing::new(pin)) };
|
||||
let parsed_expiry = match expiry { Some(s) => Some(crate::parse::parse_month_year(&s)?), None => None };
|
||||
Ok(Item::new(title, ItemCore::Card(CardCore {
|
||||
number: Some(number), holder, expiry: parsed_expiry, cvv, pin, kind: parse_card_kind(kind)?,
|
||||
})))
|
||||
}
|
||||
|
||||
pub(crate) fn build_key(
|
||||
title: String, label: Option<String>, algorithm: Option<String>,
|
||||
public_key: Option<String>, material_stdin: bool,
|
||||
) -> Result<Item> {
|
||||
use relicario_core::item_types::KeyCore;
|
||||
let key_material = resolve_secret_multiline(material_stdin, "Paste key material; end with Ctrl-D on a blank line:")?;
|
||||
if key_material.trim().is_empty() { anyhow::bail!("key material required"); }
|
||||
Ok(Item::new(title, ItemCore::Key(KeyCore {
|
||||
key_material: Zeroizing::new(key_material), label, public_key, algorithm,
|
||||
})))
|
||||
}
|
||||
|
||||
pub(crate) fn build_totp(
|
||||
title: String, issuer: Option<String>, label: Option<String>,
|
||||
secret: Option<String>, secret_stdin: bool, period: u32, digits: u8, algorithm: &str,
|
||||
) -> Result<Item> {
|
||||
use relicario_core::item_types::{TotpConfig, TotpCore, TotpKind};
|
||||
let secret_b32 = match secret {
|
||||
Some(s) => s,
|
||||
None => resolve_secret_line(secret_stdin, "TOTP secret (base32)")?,
|
||||
};
|
||||
let secret_bytes = base32_decode_lenient(&secret_b32)?;
|
||||
Ok(Item::new(title, ItemCore::Totp(TotpCore {
|
||||
config: TotpConfig {
|
||||
secret: Zeroizing::new(secret_bytes), algorithm: parse_totp_algorithm(algorithm)?,
|
||||
digits, period_seconds: period, kind: TotpKind::Totp,
|
||||
},
|
||||
issuer, label,
|
||||
})))
|
||||
}
|
||||
|
||||
pub(crate) fn build_document(
|
||||
title: String, file: PathBuf, key: &Zeroizing<[u8; 32]>, max_bytes: u64,
|
||||
) -> Result<(Item, EncryptedAttachment)> {
|
||||
use relicario_core::item_types::DocumentCore;
|
||||
use relicario_core::{encrypt_attachment, AttachmentRef};
|
||||
let bytes = std::fs::read(&file).with_context(|| format!("failed to read {}", file.display()))?;
|
||||
let enc = encrypt_attachment(&bytes, key, max_bytes)?;
|
||||
let filename = file.file_name()
|
||||
.ok_or_else(|| anyhow::anyhow!("file path has no filename: {}", file.display()))?
|
||||
.to_string_lossy().into_owned();
|
||||
let mime_type = crate::parse::guess_mime(&filename);
|
||||
let primary_attachment = enc.id.clone();
|
||||
let mut item = Item::new(title, ItemCore::Document(DocumentCore {
|
||||
filename: filename.clone(), mime_type: mime_type.clone(), primary_attachment: primary_attachment.clone(),
|
||||
}));
|
||||
item.attachments.push(AttachmentRef {
|
||||
id: primary_attachment, filename, mime_type, size: bytes.len() as u64, created: item.created,
|
||||
});
|
||||
Ok((item, enc))
|
||||
}
|
||||
|
||||
pub(crate) fn push_history(
|
||||
history: &mut FieldHistory,
|
||||
synthetic_key: &str,
|
||||
|
||||
Reference in New Issue
Block a user