feat(cli/org): org add parity for Card/Key/Totp via shared builders

This commit is contained in:
adlee-was-taken
2026-06-20 18:31:29 -04:00
parent 07862b8d44
commit 82feb49ab4
2 changed files with 110 additions and 40 deletions

View File

@@ -6,12 +6,13 @@ use std::path::Path;
use anyhow::{Context, Result};
use relicario_core::{
generate_org_key, wrap_org_key,
CollectionDef, Item, ItemCore, MemberId, OrgCollections, OrgManifest, OrgMembers, OrgMeta,
CollectionDef, Item, MemberId, OrgCollections, OrgManifest, OrgMembers, OrgMeta,
OrgRole, OrgMember,
encrypt_org_manifest,
};
use crate::org_session::atomic_write;
use crate::commands::item_build as ib;
pub fn run_init(dir: &Path, name: &str) -> Result<()> {
// Create directory structure
@@ -745,17 +746,20 @@ pub fn run_audit(
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 {
Login {
title: String,
username: Option<String>,
url: Option<String>,
password: Option<String>,
password_stdin: bool,
},
SecureNote {
title: String,
body: String,
body: Option<String>,
body_stdin: bool,
},
Identity {
title: String,
@@ -763,43 +767,56 @@ pub enum OrgAddKind {
email: 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> {
use relicario_core::item_types::{IdentityCore, LoginCore, SecureNoteCore};
use zeroize::Zeroizing;
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,
}))
fn build_org_item(kind: OrgAddKind) -> Result<Item> {
match kind {
OrgAddKind::Login { title, username, url, password, password_stdin } => {
ib::build_login(title, username, url, password, password_stdin, false, None)
}
OrgAddKind::SecureNote { title, body } => {
Item::new(title, ItemCore::SecureNote(SecureNoteCore {
body: Zeroizing::new(body),
}))
OrgAddKind::SecureNote { title, body, body_stdin } => {
ib::build_secure_note(title, body, body_stdin)
}
OrgAddKind::Identity { title, full_name, email, phone } => {
Item::new(title, ItemCore::Identity(IdentityCore {
full_name,
address: None,
phone,
email,
date_of_birth: None,
}))
ib::build_identity(title, full_name, email, phone, None)
}
};
item.tags = tags;
Ok(item)
OrgAddKind::Card { title, holder, expiry, kind, number_stdin, cvv_stdin, pin_stdin } => {
ib::build_card(title, holder, expiry, &kind, number_stdin, cvv_stdin, pin_stdin)
}
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<()> {
@@ -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.
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)?;
// Upsert the manifest entry (collection slug stored plaintext inside the