feat(cli/org): org add parity for Card/Key/Totp via shared builders
This commit is contained in:
@@ -6,12 +6,13 @@ use std::path::Path;
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use relicario_core::{
|
use relicario_core::{
|
||||||
generate_org_key, wrap_org_key,
|
generate_org_key, wrap_org_key,
|
||||||
CollectionDef, Item, ItemCore, MemberId, OrgCollections, OrgManifest, OrgMembers, OrgMeta,
|
CollectionDef, Item, MemberId, OrgCollections, OrgManifest, OrgMembers, OrgMeta,
|
||||||
OrgRole, OrgMember,
|
OrgRole, OrgMember,
|
||||||
encrypt_org_manifest,
|
encrypt_org_manifest,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::org_session::atomic_write;
|
use crate::org_session::atomic_write;
|
||||||
|
use crate::commands::item_build as ib;
|
||||||
|
|
||||||
pub fn run_init(dir: &Path, name: &str) -> Result<()> {
|
pub fn run_init(dir: &Path, name: &str) -> Result<()> {
|
||||||
// Create directory structure
|
// Create directory structure
|
||||||
@@ -745,17 +746,20 @@ pub fn run_audit(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Item kinds `org add` supports without interactive prompts.
|
/// Item kinds `org add` supports. Secrets resolve via `--*-stdin` flags or an
|
||||||
|
/// interactive prompt inside the shared `item_build` builders.
|
||||||
pub enum OrgAddKind {
|
pub enum OrgAddKind {
|
||||||
Login {
|
Login {
|
||||||
title: String,
|
title: String,
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
|
password_stdin: bool,
|
||||||
},
|
},
|
||||||
SecureNote {
|
SecureNote {
|
||||||
title: String,
|
title: String,
|
||||||
body: String,
|
body: Option<String>,
|
||||||
|
body_stdin: bool,
|
||||||
},
|
},
|
||||||
Identity {
|
Identity {
|
||||||
title: String,
|
title: String,
|
||||||
@@ -763,43 +767,56 @@ pub enum OrgAddKind {
|
|||||||
email: Option<String>,
|
email: Option<String>,
|
||||||
phone: Option<String>,
|
phone: Option<String>,
|
||||||
},
|
},
|
||||||
|
Card {
|
||||||
|
title: String,
|
||||||
|
holder: Option<String>,
|
||||||
|
expiry: Option<String>,
|
||||||
|
kind: String,
|
||||||
|
number_stdin: bool,
|
||||||
|
cvv_stdin: bool,
|
||||||
|
pin_stdin: bool,
|
||||||
|
},
|
||||||
|
Key {
|
||||||
|
title: String,
|
||||||
|
label: Option<String>,
|
||||||
|
algorithm: Option<String>,
|
||||||
|
public_key: Option<String>,
|
||||||
|
material_stdin: bool,
|
||||||
|
},
|
||||||
|
Totp {
|
||||||
|
title: String,
|
||||||
|
issuer: Option<String>,
|
||||||
|
label: Option<String>,
|
||||||
|
secret: Option<String>,
|
||||||
|
secret_stdin: bool,
|
||||||
|
period: u32,
|
||||||
|
digits: u8,
|
||||||
|
algorithm: String,
|
||||||
|
},
|
||||||
|
// Document is added later by Dev-C.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_org_item(kind: OrgAddKind, tags: Vec<String>) -> Result<Item> {
|
fn build_org_item(kind: OrgAddKind) -> Result<Item> {
|
||||||
use relicario_core::item_types::{IdentityCore, LoginCore, SecureNoteCore};
|
match kind {
|
||||||
use zeroize::Zeroizing;
|
OrgAddKind::Login { title, username, url, password, password_stdin } => {
|
||||||
|
ib::build_login(title, username, url, password, password_stdin, false, None)
|
||||||
let mut item = match kind {
|
|
||||||
OrgAddKind::Login { title, username, url, password } => {
|
|
||||||
let parsed_url = match url {
|
|
||||||
Some(s) => Some(url::Url::parse(&s).with_context(|| format!("invalid URL: {s}"))?),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
let password = password.map(Zeroizing::new);
|
|
||||||
Item::new(title, ItemCore::Login(LoginCore {
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
url: parsed_url,
|
|
||||||
totp: None,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
OrgAddKind::SecureNote { title, body } => {
|
OrgAddKind::SecureNote { title, body, body_stdin } => {
|
||||||
Item::new(title, ItemCore::SecureNote(SecureNoteCore {
|
ib::build_secure_note(title, body, body_stdin)
|
||||||
body: Zeroizing::new(body),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
OrgAddKind::Identity { title, full_name, email, phone } => {
|
OrgAddKind::Identity { title, full_name, email, phone } => {
|
||||||
Item::new(title, ItemCore::Identity(IdentityCore {
|
ib::build_identity(title, full_name, email, phone, None)
|
||||||
full_name,
|
|
||||||
address: None,
|
|
||||||
phone,
|
|
||||||
email,
|
|
||||||
date_of_birth: None,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
};
|
OrgAddKind::Card { title, holder, expiry, kind, number_stdin, cvv_stdin, pin_stdin } => {
|
||||||
item.tags = tags;
|
ib::build_card(title, holder, expiry, &kind, number_stdin, cvv_stdin, pin_stdin)
|
||||||
Ok(item)
|
}
|
||||||
|
OrgAddKind::Key { title, label, algorithm, public_key, material_stdin } => {
|
||||||
|
ib::build_key(title, label, algorithm, public_key, material_stdin)
|
||||||
|
}
|
||||||
|
OrgAddKind::Totp { title, issuer, label, secret, secret_stdin, period, digits, algorithm } => {
|
||||||
|
ib::build_totp(title, issuer, label, secret, secret_stdin, period, digits, &algorithm)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_add(dir: &Path, collection: &str, kind: OrgAddKind, tags: Vec<String>) -> Result<()> {
|
pub fn run_add(dir: &Path, collection: &str, kind: OrgAddKind, tags: Vec<String>) -> Result<()> {
|
||||||
@@ -816,7 +833,8 @@ pub fn run_add(dir: &Path, collection: &str, kind: OrgAddKind, tags: Vec<String>
|
|||||||
// …and the caller must hold a grant for it.
|
// …and the caller must hold a grant for it.
|
||||||
UnlockedOrgVault::ensure_grant(&caller, collection)?;
|
UnlockedOrgVault::ensure_grant(&caller, collection)?;
|
||||||
|
|
||||||
let item = build_org_item(kind, tags)?;
|
let mut item = build_org_item(kind)?;
|
||||||
|
item.tags = tags;
|
||||||
let item_rel = vault.save_item(collection, &item)?;
|
let item_rel = vault.save_item(collection, &item)?;
|
||||||
|
|
||||||
// Upsert the manifest entry (collection slug stored plaintext inside the
|
// Upsert the manifest entry (collection slug stored plaintext inside the
|
||||||
|
|||||||
@@ -566,13 +566,15 @@ pub(crate) enum OrgAddKind {
|
|||||||
#[arg(long)] url: Option<String>,
|
#[arg(long)] url: Option<String>,
|
||||||
#[arg(long)] password: Option<String>,
|
#[arg(long)] password: Option<String>,
|
||||||
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
||||||
|
#[arg(long)] password_stdin: bool,
|
||||||
},
|
},
|
||||||
/// A secure note.
|
/// A secure note.
|
||||||
SecureNote {
|
SecureNote {
|
||||||
#[arg(long)] collection: String,
|
#[arg(long)] collection: String,
|
||||||
#[arg(long)] title: String,
|
#[arg(long)] title: String,
|
||||||
#[arg(long)] body: String,
|
#[arg(long)] body: Option<String>,
|
||||||
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
||||||
|
#[arg(long)] body_stdin: bool,
|
||||||
},
|
},
|
||||||
/// An identity record.
|
/// An identity record.
|
||||||
Identity {
|
Identity {
|
||||||
@@ -583,6 +585,41 @@ pub(crate) enum OrgAddKind {
|
|||||||
#[arg(long)] phone: Option<String>,
|
#[arg(long)] phone: Option<String>,
|
||||||
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
||||||
},
|
},
|
||||||
|
/// A payment card (number / cvv / pin entered via --*-stdin or prompt).
|
||||||
|
Card {
|
||||||
|
#[arg(long)] collection: String,
|
||||||
|
#[arg(long)] title: String,
|
||||||
|
#[arg(long)] holder: Option<String>,
|
||||||
|
#[arg(long)] expiry: Option<String>,
|
||||||
|
#[arg(long, default_value = "credit")] kind: String,
|
||||||
|
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
||||||
|
#[arg(long)] number_stdin: bool,
|
||||||
|
#[arg(long)] cvv_stdin: bool,
|
||||||
|
#[arg(long)] pin_stdin: bool,
|
||||||
|
},
|
||||||
|
/// A key / credential blob (material entered via --material-stdin or prompt).
|
||||||
|
Key {
|
||||||
|
#[arg(long)] collection: String,
|
||||||
|
#[arg(long)] title: String,
|
||||||
|
#[arg(long)] label: Option<String>,
|
||||||
|
#[arg(long)] algorithm: Option<String>,
|
||||||
|
#[arg(long)] public_key: Option<String>,
|
||||||
|
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
||||||
|
#[arg(long)] material_stdin: bool,
|
||||||
|
},
|
||||||
|
/// A TOTP authenticator (base32 secret via --secret or --secret-stdin).
|
||||||
|
Totp {
|
||||||
|
#[arg(long)] collection: String,
|
||||||
|
#[arg(long)] title: String,
|
||||||
|
#[arg(long)] issuer: Option<String>,
|
||||||
|
#[arg(long)] label: Option<String>,
|
||||||
|
#[arg(long)] secret: Option<String>,
|
||||||
|
#[arg(long, default_value_t = 30)] period: u32,
|
||||||
|
#[arg(long, default_value_t = 6)] digits: u8,
|
||||||
|
#[arg(long, default_value = "sha1")] algorithm: String,
|
||||||
|
#[arg(long, value_delimiter = ',')] tags: Vec<String>,
|
||||||
|
#[arg(long)] secret_stdin: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
@@ -676,14 +713,14 @@ fn main() -> Result<()> {
|
|||||||
OrgCommands::Add { kind } => {
|
OrgCommands::Add { kind } => {
|
||||||
let d = crate::org_session::org_dir(dir_path)?;
|
let d = crate::org_session::org_dir(dir_path)?;
|
||||||
let (collection, add_kind, tags) = match kind {
|
let (collection, add_kind, tags) = match kind {
|
||||||
OrgAddKind::Login { collection, title, username, url, password, tags } => (
|
OrgAddKind::Login { collection, title, username, url, password, tags, password_stdin } => (
|
||||||
collection,
|
collection,
|
||||||
commands::org::OrgAddKind::Login { title, username, url, password },
|
commands::org::OrgAddKind::Login { title, username, url, password, password_stdin },
|
||||||
tags,
|
tags,
|
||||||
),
|
),
|
||||||
OrgAddKind::SecureNote { collection, title, body, tags } => (
|
OrgAddKind::SecureNote { collection, title, body, tags, body_stdin } => (
|
||||||
collection,
|
collection,
|
||||||
commands::org::OrgAddKind::SecureNote { title, body },
|
commands::org::OrgAddKind::SecureNote { title, body, body_stdin },
|
||||||
tags,
|
tags,
|
||||||
),
|
),
|
||||||
OrgAddKind::Identity { collection, title, full_name, email, phone, tags } => (
|
OrgAddKind::Identity { collection, title, full_name, email, phone, tags } => (
|
||||||
@@ -691,6 +728,21 @@ fn main() -> Result<()> {
|
|||||||
commands::org::OrgAddKind::Identity { title, full_name, email, phone },
|
commands::org::OrgAddKind::Identity { title, full_name, email, phone },
|
||||||
tags,
|
tags,
|
||||||
),
|
),
|
||||||
|
OrgAddKind::Card { collection, title, holder, expiry, kind, tags, number_stdin, cvv_stdin, pin_stdin } => (
|
||||||
|
collection,
|
||||||
|
commands::org::OrgAddKind::Card { title, holder, expiry, kind, number_stdin, cvv_stdin, pin_stdin },
|
||||||
|
tags,
|
||||||
|
),
|
||||||
|
OrgAddKind::Key { collection, title, label, algorithm, public_key, tags, material_stdin } => (
|
||||||
|
collection,
|
||||||
|
commands::org::OrgAddKind::Key { title, label, algorithm, public_key, material_stdin },
|
||||||
|
tags,
|
||||||
|
),
|
||||||
|
OrgAddKind::Totp { collection, title, issuer, label, secret, period, digits, algorithm, tags, secret_stdin } => (
|
||||||
|
collection,
|
||||||
|
commands::org::OrgAddKind::Totp { title, issuer, label, secret, secret_stdin, period, digits, algorithm },
|
||||||
|
tags,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
commands::org::run_add(&d, &collection, add_kind, tags)?;
|
commands::org::run_add(&d, &collection, add_kind, tags)?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user