Add export
command
This commit is contained in:
parent
7b58e9e9a6
commit
5df67e772c
4 changed files with 151 additions and 14 deletions
63
src/cli/export_command.rs
Normal file
63
src/cli/export_command.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// 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, path::PathBuf};
|
||||||
|
|
||||||
|
use clap::{Args, ValueEnum};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
password::{BitWardenPasswords, Passwords},
|
||||||
|
LprsError, LprsResult, RunCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, ValueEnum)]
|
||||||
|
pub enum ExportFormat {
|
||||||
|
Lprs,
|
||||||
|
BitWarden,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
pub struct Export {
|
||||||
|
/// The path to export to
|
||||||
|
path: PathBuf,
|
||||||
|
/// Format to export passwords in
|
||||||
|
#[arg(short, long, value_name = "FORMAT", default_value_t= ExportFormat::Lprs)]
|
||||||
|
format: ExportFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for ExportFormat {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.to_possible_value()
|
||||||
|
.expect("There is no skiped values")
|
||||||
|
.get_name()
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunCommand for Export {
|
||||||
|
fn run(&self, password_manager: Passwords) -> LprsResult<()> {
|
||||||
|
let exported_data = match self.format {
|
||||||
|
ExportFormat::Lprs => serde_json::to_string(&password_manager.encrypt()?.passwords),
|
||||||
|
ExportFormat::BitWarden => {
|
||||||
|
serde_json::to_string(&BitWardenPasswords::from(password_manager))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.map_err(LprsError::from)?;
|
||||||
|
|
||||||
|
fs::write(&self.path, exported_data).map_err(LprsError::from)
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ use crate::{
|
||||||
pub mod add_command;
|
pub mod add_command;
|
||||||
pub mod clean_command;
|
pub mod clean_command;
|
||||||
pub mod edit_command;
|
pub mod edit_command;
|
||||||
|
pub mod export_command;
|
||||||
pub mod gen_command;
|
pub mod gen_command;
|
||||||
pub mod list_command;
|
pub mod list_command;
|
||||||
pub mod remove_command;
|
pub mod remove_command;
|
||||||
|
@ -38,6 +39,7 @@ crate::create_commands!(
|
||||||
"Clean the password file", Clean => clean_command::Clean
|
"Clean the password file", Clean => clean_command::Clean
|
||||||
"Edit the password content", Edit => edit_command::Edit
|
"Edit the password content", Edit => edit_command::Edit
|
||||||
"Generate password", Gen => gen_command::Gen
|
"Generate password", Gen => gen_command::Gen
|
||||||
|
"Export the passwords", Export => export_command::Export
|
||||||
// TODO: Export command
|
// TODO: Export command
|
||||||
// TODO: Import command
|
// TODO: Import command
|
||||||
);
|
);
|
||||||
|
|
71
src/password/bitwarden.rs
Normal file
71
src/password/bitwarden.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::{Password, Passwords};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct BitWardenLoginData {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
pub uris: Option<Vec<BitWardenUri>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct BitWardenUri {
|
||||||
|
#[serde(rename = "match")]
|
||||||
|
pub mt: Option<i32>,
|
||||||
|
pub uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Deserialize, Serialize)]
|
||||||
|
pub struct BitWardenFolder {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct BitWardenPassword {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub ty: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub login: BitWardenLoginData,
|
||||||
|
pub notes: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The bitwarden password struct
|
||||||
|
#[derive(Default, Deserialize, Serialize)]
|
||||||
|
pub struct BitWardenPasswords {
|
||||||
|
pub encrypted: bool,
|
||||||
|
pub folders: Vec<BitWardenFolder>,
|
||||||
|
pub items: Vec<BitWardenPassword>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Password> for BitWardenPassword {
|
||||||
|
fn from(value: Password) -> Self {
|
||||||
|
Self {
|
||||||
|
ty: 1,
|
||||||
|
name: value.name,
|
||||||
|
login: BitWardenLoginData {
|
||||||
|
username: value.username,
|
||||||
|
password: value.password,
|
||||||
|
uris: value
|
||||||
|
.service
|
||||||
|
.map(|s| vec![BitWardenUri { mt: None, uri: s }]),
|
||||||
|
},
|
||||||
|
notes: value.note,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Passwords> for BitWardenPasswords {
|
||||||
|
fn from(value: Passwords) -> Self {
|
||||||
|
Self {
|
||||||
|
encrypted: false,
|
||||||
|
folders: Vec::new(),
|
||||||
|
items: value
|
||||||
|
.passwords
|
||||||
|
.into_iter()
|
||||||
|
.map(BitWardenPassword::from)
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,23 +22,13 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::{LprsError, LprsResult};
|
use crate::{LprsError, LprsResult};
|
||||||
|
|
||||||
pub mod cipher;
|
pub mod cipher;
|
||||||
|
|
||||||
|
mod bitwarden;
|
||||||
mod validator;
|
mod validator;
|
||||||
|
|
||||||
|
pub use bitwarden::*;
|
||||||
pub use validator::*;
|
pub use validator::*;
|
||||||
|
|
||||||
/// The passwords manager
|
|
||||||
#[derive(Default, Deserialize, Serialize)]
|
|
||||||
pub struct Passwords {
|
|
||||||
/// Hash of the master password
|
|
||||||
#[serde(skip)]
|
|
||||||
pub master_password: Vec<u8>,
|
|
||||||
/// The json passwords file
|
|
||||||
#[serde(skip)]
|
|
||||||
pub passwords_file: PathBuf,
|
|
||||||
/// The passwords
|
|
||||||
pub passwords: Vec<Password>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The password struct
|
/// The password struct
|
||||||
#[serde_with_macros::skip_serializing_none]
|
#[serde_with_macros::skip_serializing_none]
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, Parser)]
|
#[derive(Clone, Debug, Deserialize, Serialize, Parser)]
|
||||||
|
@ -60,6 +50,17 @@ pub struct Password {
|
||||||
pub note: Option<String>,
|
pub note: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The passwords manager
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Passwords {
|
||||||
|
/// Hash of the master password
|
||||||
|
pub master_password: Vec<u8>,
|
||||||
|
/// The json passwords file
|
||||||
|
pub passwords_file: PathBuf,
|
||||||
|
/// The passwords
|
||||||
|
pub passwords: Vec<Password>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Password {
|
impl Password {
|
||||||
/// Encrypt the password data
|
/// Encrypt the password data
|
||||||
pub fn encrypt(self, master_password: &[u8]) -> LprsResult<Self> {
|
pub fn encrypt(self, master_password: &[u8]) -> LprsResult<Self> {
|
||||||
|
@ -111,7 +112,7 @@ impl Passwords {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt the passwords
|
/// Encrypt the passwords
|
||||||
fn encrypt(self) -> LprsResult<Self> {
|
pub fn encrypt(self) -> LprsResult<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
passwords: self
|
passwords: self
|
||||||
.passwords
|
.passwords
|
||||||
|
|
Loading…
Reference in a new issue