Add additive KDBX importer
This commit is contained in:
@@ -26,6 +26,7 @@ use ironstorage::{
|
||||
crypto::KeyStore,
|
||||
generate::{GeneratorConfig, PasswordGenerator},
|
||||
git::{GitChangeKind, GitIdentity, GitRepository, PullOutcome},
|
||||
kdbx::KdbxImporter,
|
||||
mutation::{
|
||||
MutationError, NoGitTreeCommitter, TreeCommit, TreeCommitError, TreeCommitter, TreeMutator,
|
||||
},
|
||||
@@ -177,6 +178,7 @@ fn needs_secret_store(request: &CommandRequest) -> bool {
|
||||
| CommandRequest::Remove(_)
|
||||
| CommandRequest::Move(_)
|
||||
| CommandRequest::Copy(_)
|
||||
| CommandRequest::ImportKdbx(_)
|
||||
| CommandRequest::Otp(
|
||||
OtpRequest::Code(_)
|
||||
| OtpRequest::Insert(_)
|
||||
@@ -651,6 +653,32 @@ fn execute_secure_with_services<
|
||||
interaction,
|
||||
stderr,
|
||||
),
|
||||
CommandRequest::ImportKdbx(request) => {
|
||||
let password = match interaction.read_kdbx_password(request.source()) {
|
||||
Ok(password) => password,
|
||||
Err(error) => return operation_error(stderr, error),
|
||||
};
|
||||
match KdbxImporter::new(&repository, &keys).import(
|
||||
request,
|
||||
password,
|
||||
secrets,
|
||||
git_identity(),
|
||||
) {
|
||||
Ok(outcome) => {
|
||||
writeln!(
|
||||
stdout,
|
||||
"KDBX import: {} added, {} updated, {} unchanged, {} skipped",
|
||||
outcome.added(),
|
||||
outcome.updated(),
|
||||
outcome.unchanged(),
|
||||
outcome.skipped()
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
Ok(EXIT_SUCCESS)
|
||||
}
|
||||
Err(error) => operation_error(stderr, error),
|
||||
}
|
||||
}
|
||||
CommandRequest::Otp(request) => execute_otp(
|
||||
config,
|
||||
request,
|
||||
@@ -1026,6 +1054,8 @@ trait OtpInteraction {
|
||||
}
|
||||
|
||||
trait CliInteraction: OtpInteraction {
|
||||
fn read_kdbx_password(&mut self, source: &Path) -> Result<SecretBytes, CliInteractionError>;
|
||||
|
||||
fn read_insert(
|
||||
&mut self,
|
||||
plan: InputPlan,
|
||||
@@ -1130,6 +1160,12 @@ impl OtpInteraction for NativeOtpInteraction {
|
||||
}
|
||||
|
||||
impl CliInteraction for NativeOtpInteraction {
|
||||
fn read_kdbx_password(&mut self, source: &Path) -> Result<SecretBytes, CliInteractionError> {
|
||||
rpassword::prompt_password(format!("Enter password for {}: ", source.display()))
|
||||
.map(|password| SecretBytes::new(password.into_bytes()))
|
||||
.map_err(|_| CliInteractionError::Input)
|
||||
}
|
||||
|
||||
fn read_insert(
|
||||
&mut self,
|
||||
plan: InputPlan,
|
||||
@@ -1213,6 +1249,10 @@ impl OtpInteraction for UnavailableOtpInteraction {
|
||||
|
||||
#[cfg(test)]
|
||||
impl CliInteraction for UnavailableOtpInteraction {
|
||||
fn read_kdbx_password(&mut self, _source: &Path) -> Result<SecretBytes, CliInteractionError> {
|
||||
Err(CliInteractionError::Input)
|
||||
}
|
||||
|
||||
fn read_insert(
|
||||
&mut self,
|
||||
_plan: InputPlan,
|
||||
@@ -1611,6 +1651,7 @@ mod tests {
|
||||
inputs: VecDeque<OtpInput>,
|
||||
insert_inputs: VecDeque<ironstorage::write::InsertContent>,
|
||||
edit_replacements: VecDeque<SecretBytes>,
|
||||
kdbx_passwords: VecDeque<SecretBytes>,
|
||||
decisions: VecDeque<OverwriteDecision>,
|
||||
plans: Vec<InputPlan>,
|
||||
prompts: Vec<String>,
|
||||
@@ -1644,6 +1685,15 @@ mod tests {
|
||||
}
|
||||
|
||||
impl super::CliInteraction for MemoryOtpInteraction {
|
||||
fn read_kdbx_password(
|
||||
&mut self,
|
||||
_source: &std::path::Path,
|
||||
) -> Result<SecretBytes, super::CliInteractionError> {
|
||||
self.kdbx_passwords
|
||||
.pop_front()
|
||||
.ok_or(super::CliInteractionError::Input)
|
||||
}
|
||||
|
||||
fn read_insert(
|
||||
&mut self,
|
||||
_plan: InputPlan,
|
||||
|
||||
Reference in New Issue
Block a user