Add import command

This commit is contained in:
TheAwiteb 2023-12-29 11:37:53 +03:00
parent aa66bd7fda
commit f888e83525
No known key found for this signature in database
GPG key ID: ABF818BD15DC2D34
3 changed files with 105 additions and 2 deletions

88
src/cli/import_command.rs Normal file
View file

@ -0,0 +1,88 @@
// Lprs - A local CLI password manager
// Copyright (C) 2024 Awiteb
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
use std::{fs::File, io::Error as IoError, io::ErrorKind as IoErrorKind, path::PathBuf};
use clap::Args;
use crate::{
password::{BitWardenPasswords, Format, Password, Passwords},
LprsError, LprsResult, RunCommand,
};
#[derive(Debug, Args)]
#[command(author, version, about, long_about = None)]
pub struct Import {
/// The file path to import from
path: PathBuf,
/// The format to import from
#[arg(short, long, default_value_t = Format::Lprs)]
format: Format,
}
impl RunCommand for Import {
fn run(&self, mut password_manager: Passwords) -> LprsResult<()> {
if self.path.exists() {
if self
.path
.extension()
.is_some_and(|e| e.to_string_lossy().eq_ignore_ascii_case("json"))
{
let imported_passwords_len = match self.format {
Format::Lprs => {
let passwords = Passwords::try_reload(
self.path.to_path_buf(),
password_manager.master_password.to_vec(),
)?;
let passwords_len = passwords.passwords.len();
password_manager.passwords.extend(passwords.passwords);
password_manager.try_export()?;
passwords_len
}
Format::BitWarden => {
let passwords: BitWardenPasswords =
serde_json::from_reader(File::open(&self.path)?)?;
let passwords_len = passwords.items.len();
password_manager
.passwords
.extend(passwords.items.into_iter().map(Password::from));
password_manager.try_export()?;
passwords_len
}
};
println!(
"{imported_passwords_len} password{s} were imported successfully",
s = if imported_passwords_len >= 2 { "s" } else { "" }
);
Ok(())
} else {
Err(LprsError::Io(IoError::new(
IoErrorKind::InvalidInput,
format!("file `{}` is not a json file", self.path.display()),
)))
}
} else {
Err(LprsError::Io(IoError::new(
IoErrorKind::NotFound,
format!("file `{}` not found", self.path.display()),
)))
}
}
}

View file

@ -28,6 +28,7 @@ pub mod clean_command;
pub mod edit_command;
pub mod export_command;
pub mod gen_command;
pub mod import_command;
pub mod list_command;
pub mod remove_command;
@ -40,8 +41,7 @@ crate::create_commands!(
"Edit the password content", Edit => edit_command::Edit
"Generate password", Gen => gen_command::Gen
"Export the passwords", Export => export_command::Export
// TODO: Export command
// TODO: Import command
"Import passwords", Import => import_command::Import
);
#[derive(Parser, Debug)]

View file

@ -39,6 +39,21 @@ pub struct BitWardenPasswords {
pub items: Vec<BitWardenPassword>,
}
impl From<BitWardenPassword> for Password {
fn from(value: BitWardenPassword) -> Self {
Self {
name: value.name,
username: value.login.username,
password: value.login.password,
service: value
.login
.uris
.and_then(|p| p.first().map(|u| u.uri.clone())),
note: value.notes,
}
}
}
impl From<Password> for BitWardenPassword {
fn from(value: Password) -> Self {
Self {