refactor: Rename Passwords Vaults

This commit is contained in:
Awiteb 2024-03-17 11:35:47 +03:00
parent e3788a049d
commit f6aaecb9cf
Signed by: awiteb
GPG key ID: 3F6B55640AA6682F
15 changed files with 302 additions and 109 deletions

View file

@ -17,7 +17,7 @@
use clap::Args; use clap::Args;
use crate::{ use crate::{
password::{Password, Passwords}, password::{Vault, Vaults},
LprsResult, RunCommand, LprsResult, RunCommand,
}; };
@ -25,12 +25,12 @@ use crate::{
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Add { pub struct Add {
#[command(flatten)] #[command(flatten)]
password_info: Password, vault_info: Vault<Plain>,
} }
impl RunCommand for Add { impl RunCommand for Add {
fn run(&self, mut password_manager: Passwords) -> LprsResult<()> { fn run(&self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
password_manager.add_password(self.password_info.clone()); vault_manager.add_vault(self.vault_info.clone());
password_manager.try_export() vault_manager.try_export()
} }
} }

View file

@ -18,14 +18,14 @@ use std::fs;
use clap::Args; use clap::Args;
use crate::{password::Passwords, LprsError, LprsResult, RunCommand}; use crate::{password::Vaults, LprsError, LprsResult, RunCommand};
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Clean {} pub struct Clean {}
impl RunCommand for Clean { impl RunCommand for Clean {
fn run(&self, password_manager: Passwords) -> LprsResult<()> { fn run(&self, password_manager: Vaults) -> LprsResult<()> {
fs::write(password_manager.passwords_file, "[]").map_err(LprsError::Io) fs::write(password_manager.passwords_file, "[]").map_err(LprsError::Io)
} }
} }

View file

@ -19,7 +19,7 @@ use std::num::NonZeroU64;
use clap::Args; use clap::Args;
use crate::{ use crate::{
password::{Password, Passwords}, password::{Vault, Vaults},
LprsError, LprsResult, RunCommand, LprsError, LprsResult, RunCommand,
}; };
@ -30,27 +30,27 @@ pub struct Edit {
index: NonZeroU64, index: NonZeroU64,
#[arg(short, long)] #[arg(short, long)]
/// The new password name /// The new vault name
name: Option<String>, name: Option<String>,
#[arg(short, long)] #[arg(short, long)]
/// The new password username /// The new vault username
username: Option<String>, username: Option<String>,
#[arg(short, long)] #[arg(short, long)]
/// The new password /// The new password
password: Option<String>, password: Option<String>,
#[arg(short, long)] #[arg(short, long)]
/// The new password service /// The new vault service
service: Option<String>, service: Option<String>,
#[arg(short = 'o', long)] #[arg(short = 'o', long)]
/// The new password note /// The new vault note
note: Option<String>, note: Option<String>,
} }
impl RunCommand for Edit { impl RunCommand for Edit {
fn run(&self, mut password_manager: Passwords) -> LprsResult<()> { fn run(&self, mut password_manager: Vaults) -> LprsResult<()> {
let index = self.index.get() as usize; let index = self.index.get() as usize;
if let Some(password) = password_manager.passwords.get_mut(index - 1) { if let Some(vault) = vault_manager.vaults.get_mut(index - 1) {
if self.name.is_none() if self.name.is_none()
&& self.username.is_none() && self.username.is_none()
&& self.password.is_none() && self.password.is_none()
@ -61,26 +61,18 @@ impl RunCommand for Edit {
"You must edit one option at least".to_owned(), "You must edit one option at least".to_owned(),
)) ))
} else { } else {
*password = Password { *vault = Vault::<Plain>::new(
name: self.name.as_ref().unwrap_or(&password.name).to_string(), self.name.as_ref().unwrap_or(&vault.name),
username: self self.username.as_ref().unwrap_or(&vault.username),
.username self.password.as_ref().unwrap_or(&vault.password),
.as_ref() self.service.as_ref().or(vault.service.as_ref()),
.unwrap_or(&password.username) self.note.as_ref().or(vault.note.as_ref()),
.to_string(), );
password: self vault_manager.try_export()
.password
.as_ref()
.unwrap_or(&password.password)
.to_string(),
service: self.service.as_ref().or(password.service.as_ref()).cloned(),
note: self.note.as_ref().or(password.note.as_ref()).cloned(),
};
password_manager.try_export()
} }
} else { } else {
Err(LprsError::InvalidPasswordIndex(format!( Err(LprsError::InvalidVaultIndex(format!(
"The index `{}` is greater than the passwords count {}", "The index `{}` is greater than the vaults count {}",
self.index, self.index,
password_manager.passwords.len() password_manager.passwords.len()
))) )))

View file

@ -19,7 +19,7 @@ use std::{fs, io::Error as IoError, io::ErrorKind as IoErrorKind, path::PathBuf}
use clap::Args; use clap::Args;
use crate::{ use crate::{
password::{BitWardenPasswords, Format, Passwords}, password::{BitWardenPasswords, Format, Vaults},
LprsError, LprsResult, RunCommand, LprsError, LprsResult, RunCommand,
}; };
@ -28,13 +28,13 @@ use crate::{
pub struct Export { pub struct Export {
/// The path to export to /// The path to export to
path: PathBuf, path: PathBuf,
/// Format to export passwords in /// Format to export vaults in
#[arg(short, long, value_name = "FORMAT", default_value_t= Format::Lprs)] #[arg(short, long, value_name = "FORMAT", default_value_t= Format::Lprs)]
format: Format, format: Format,
} }
impl RunCommand for Export { impl RunCommand for Export {
fn run(&self, password_manager: Passwords) -> LprsResult<()> { fn run(&self, password_manager: Vaults) -> LprsResult<()> {
if self if self
.path .path
.extension() .extension()

View file

@ -18,7 +18,7 @@ use std::num::NonZeroU64;
use clap::Args; use clap::Args;
use crate::{password::Passwords, LprsError, LprsResult, RunCommand}; use crate::{password::Vaults, LprsError, LprsResult, RunCommand};
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -42,7 +42,7 @@ pub struct Gen {
} }
impl RunCommand for Gen { impl RunCommand for Gen {
fn run(&self, _password_manager: Passwords) -> LprsResult<()> { fn run(&self, _password_manager: Vaults) -> LprsResult<()> {
if self.uppercase || self.lowercase || self.numbers || self.symbols { if self.uppercase || self.lowercase || self.numbers || self.symbols {
println!( println!(
"{}", "{}",

View file

@ -19,7 +19,7 @@ use std::{fs::File, io::Error as IoError, io::ErrorKind as IoErrorKind, path::Pa
use clap::Args; use clap::Args;
use crate::{ use crate::{
password::{BitWardenPasswords, Format, Password, Passwords}, password::{BitWardenPasswords, Format, Vault, Vaults},
LprsError, LprsResult, RunCommand, LprsError, LprsResult, RunCommand,
}; };
@ -35,7 +35,7 @@ pub struct Import {
} }
impl RunCommand for Import { impl RunCommand for Import {
fn run(&self, mut password_manager: Passwords) -> LprsResult<()> { fn run(&self, mut password_manager: Vaults) -> LprsResult<()> {
if self.path.exists() { if self.path.exists() {
if self if self
.path .path
@ -44,30 +44,30 @@ impl RunCommand for Import {
{ {
let imported_passwords_len = match self.format { let imported_passwords_len = match self.format {
Format::Lprs => { Format::Lprs => {
let passwords = Passwords::try_reload( let vaults = Vaults::try_reload(
self.path.to_path_buf(), self.path.to_path_buf(),
password_manager.master_password.to_vec(), password_manager.master_password.to_vec(),
)?; )?;
let passwords_len = passwords.passwords.len(); let vaults_len = vaults.vaults.len();
password_manager.passwords.extend(passwords.passwords); vault_manager.vaults.extend(vaults.vaults);
password_manager.try_export()?; vault_manager.try_export()?;
passwords_len vaults_len
} }
Format::BitWarden => { Format::BitWarden => {
let passwords: BitWardenPasswords = let vaults: BitWardenPasswords =
serde_json::from_reader(File::open(&self.path)?)?; serde_json::from_reader(File::open(&self.path)?)?;
let passwords_len = passwords.items.len(); let vaults_len = vaults.items.len();
password_manager vault_manager
.passwords .vaults
.extend(passwords.items.into_iter().map(Password::from)); .extend(vaults.items.into_iter().map(Vault::from));
password_manager.try_export()?; vault_manager.try_export()?;
passwords_len vaults_len
} }
}; };
println!( println!(
"{imported_passwords_len} password{s} were imported successfully", "{imported_passwords_len} vault{s} were imported successfully",
s = if imported_passwords_len >= 2 { "s" } else { "" } s = if imported_passwords_len >= 2 { "s" } else { "" }
); );

View file

@ -20,7 +20,7 @@ use clap::Args;
use comfy_table::Table; use comfy_table::Table;
use regex::Regex; use regex::Regex;
use crate::{password::Passwords, LprsError, LprsResult, RunCommand}; use crate::{password::Vaults, LprsError, LprsResult, RunCommand};
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -49,7 +49,7 @@ pub struct List {
} }
impl RunCommand for List { impl RunCommand for List {
fn run(&self, password_manager: Passwords) -> LprsResult<()> { fn run(&self, password_manager: Vaults) -> LprsResult<()> {
if password_manager.passwords.is_empty() { if password_manager.passwords.is_empty() {
Err(LprsError::Other( Err(LprsError::Other(
"Looks like there is no passwords to list".to_owned(), "Looks like there is no passwords to list".to_owned(),
@ -77,8 +77,8 @@ impl RunCommand for List {
let re = Regex::new(self.search.as_deref().unwrap_or("."))?; let re = Regex::new(self.search.as_deref().unwrap_or("."))?;
table.set_header(header); table.set_header(header);
let passwords = password_manager let vaults = vault_manager
.passwords .vaults
.iter() .iter()
.enumerate() .enumerate()
.filter(|(idx, pass)| { .filter(|(idx, pass)| {
@ -112,24 +112,24 @@ impl RunCommand for List {
true true
}); });
for (idx, password) in passwords { for (idx, vault) in vaults {
let hide_password = "*".repeat(password.password.chars().count()); let hide_password = "*".repeat(vault.password.chars().count());
let idx = (idx + 1).to_string(); let idx = (idx + 1).to_string();
let mut row = vec![ let mut row = vec![
idx.as_str(), idx.as_str(),
password.name.as_str(), vault.name.as_str(),
password.username.as_str(), vault.username.as_str(),
if self.unhide_password { if self.unhide_password {
password.password.as_str() vault.password.as_str()
} else { } else {
hide_password.as_str() hide_password.as_str()
}, },
]; ];
if self.with_service { if self.with_service {
row.push(password.service.as_deref().unwrap_or("Not Set")) row.push(vault.service.as_deref().unwrap_or("Not Set"))
} }
if self.with_note { if self.with_note {
row.push(password.note.as_deref().unwrap_or("Not Set")) row.push(vault.note.as_deref().unwrap_or("Not Set"))
} }
table.add_row(row); table.add_row(row);
} }

View file

@ -19,7 +19,7 @@ use std::path::PathBuf;
use clap::Parser; use clap::Parser;
use crate::{ use crate::{
password::{self, Passwords}, password::{self, Vaults},
LprsError, LprsResult, RunCommand, LprsError, LprsResult, RunCommand,
}; };
@ -47,7 +47,7 @@ crate::create_commands!(
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Cli { pub struct Cli {
/// The passwords json file, default: $HOME/.local/share/lprs/passwords.json /// The vaults json file
#[arg(short, long)] #[arg(short, long)]
passwords_file: Option<PathBuf>, passwords_file: Option<PathBuf>,
@ -64,33 +64,30 @@ impl Cli {
} else { } else {
crate::utils::passwords_file()? crate::utils::passwords_file()?
}; };
log::debug!( log::debug!("Getting the vaults file: {}", vaults_file.to_string_lossy());
"Getting password file: {}", let vault_manager = if matches!(self.command, Commands::Clean(..) | Commands::Gen(..)) {
passwords_file.to_string_lossy() Vaults {
);
let password_manager = if matches!(self.command, Commands::Clean(..) | Commands::Gen(..)) {
Passwords {
passwords_file, passwords_file,
..Default::default() ..Default::default()
} }
} else { } else {
let password = scanpw::scanpw!("Master Password: "); let master_password = scanpw::scanpw!("Master Password: ");
if password::is_new_password_file(&passwords_file)? { if vault::is_new_vaults_file(&vaults_file)? {
let analyzed = passwords::analyzer::analyze(&password); let analyzed = passwords::analyzer::analyze(&master_password);
if analyzed.length() < 15 { if analyzed.length() < 15 {
return Err(LprsError::WeakPassword( return Err(LprsError::WeakPassword(
"The password length must be beggier then 15".to_owned(), "The master password length must be beggier then 15".to_owned(),
)); ));
} else if passwords::scorer::score(&analyzed) < 80.0 { } else if passwords::scorer::score(&analyzed) < 80.0 {
return Err(LprsError::WeakPassword( return Err(LprsError::WeakPassword(
"Your password is not stronge enough".to_owned(), "Your master password is not stronge enough".to_owned(),
)); ));
} }
} }
let master_password = sha256::digest(password); let master_password = sha256::digest(master_password);
Passwords::try_reload( Vaults::try_reload(
passwords_file, passwords_file,
master_password.into_bytes().into_iter().take(32).collect(), master_password.into_bytes().into_iter().take(32).collect(),
)? )?

View file

@ -18,7 +18,7 @@ use std::num::NonZeroU64;
use clap::Args; use clap::Args;
use crate::{password::Passwords, LprsError, LprsResult, RunCommand}; use crate::{password::Vaults, LprsError, LprsResult, RunCommand};
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -32,7 +32,7 @@ pub struct Remove {
} }
impl RunCommand for Remove { impl RunCommand for Remove {
fn run(&self, mut password_manager: Passwords) -> LprsResult<()> { fn run(&self, mut password_manager: Vaults) -> LprsResult<()> {
let index = (self.index.get() - 1) as usize; let index = (self.index.get() - 1) as usize;
if index > password_manager.passwords.len() { if index > password_manager.passwords.len() {
if !self.force { if !self.force {

View file

@ -40,7 +40,7 @@
/// impl crate::RunCommand for TestCommands { /// impl crate::RunCommand for TestCommands {
/// fn run( /// fn run(
/// &self, /// &self,
/// password_manager: crate::password::Passwords, /// vault_manager: crate::vault::Vaults,
/// ) -> crate::LprsResult<()> { /// ) -> crate::LprsResult<()> {
/// match self { /// match self {
/// Self::Test(command) => command.run(password_manager), /// Self::Test(command) => command.run(password_manager),
@ -63,7 +63,7 @@ macro_rules! create_commands {
#[automatically_derived] #[automatically_derived]
impl $crate::RunCommand for $enum_name{ impl $crate::RunCommand for $enum_name{
fn run(&self, password_manager: $crate::password::Passwords) -> $crate::LprsResult<()> { fn run(&self, password_manager: $crate::password::Vaults) -> $crate::LprsResult<()> {
match self { match self {
$( $(
Self::$varint(command) => command.run(password_manager), Self::$varint(command) => command.run(password_manager),

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::{Password, Passwords}; use super::{Vault, Vaults};
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BitWardenLoginData { pub struct BitWardenLoginData {
@ -39,7 +39,7 @@ pub struct BitWardenPasswords {
pub items: Vec<BitWardenPassword>, pub items: Vec<BitWardenPassword>,
} }
impl From<BitWardenPassword> for Password { impl From<BitWardenPassword> for Vault {
fn from(value: BitWardenPassword) -> Self { fn from(value: BitWardenPassword) -> Self {
Self { Self {
name: value.name, name: value.name,
@ -59,8 +59,8 @@ impl From<BitWardenPassword> for Password {
} }
} }
impl From<Password> for BitWardenPassword { impl From<Vault> for BitWardenPassword {
fn from(value: Password) -> Self { fn from(value: Vault) -> Self {
Self { Self {
ty: 1, ty: 1,
name: value.name, name: value.name,
@ -76,8 +76,8 @@ impl From<Password> for BitWardenPassword {
} }
} }
impl From<Passwords> for BitWardenPasswords { impl From<Vaults> for BitWardenPasswords {
fn from(value: Passwords) -> Self { fn from(value: Vaults) -> Self {
Self { Self {
encrypted: false, encrypted: false,
folders: Vec::new(), folders: Vec::new(),

View file

@ -38,7 +38,7 @@ pub enum Format {
/// 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)]
pub struct Password { pub struct Vault {
/// The name of the password /// The name of the password
#[arg(short, long)] #[arg(short, long)]
pub name: String, pub name: String,
@ -58,16 +58,16 @@ pub struct Password {
/// The passwords manager /// The passwords manager
#[derive(Default)] #[derive(Default)]
pub struct Passwords { pub struct Vaults {
/// Hash of the master password /// Hash of the master password
pub master_password: Vec<u8>, pub master_password: Vec<u8>,
/// The json passwords file /// The json passwords file
pub passwords_file: PathBuf, pub passwords_file: PathBuf,
/// The passwords /// The passwords
pub passwords: Vec<Password>, pub passwords: Vec<Vault>,
} }
impl Password { impl Vault {
/// 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> {
Ok(Self { Ok(Self {
@ -103,13 +103,9 @@ impl Password {
} }
} }
impl Passwords { impl Vaults {
/// Create new Passwords instnce /// Create new Passwords instnce
pub fn new( pub fn new(master_password: Vec<u8>, passwords_file: PathBuf, passwords: Vec<Vault>) -> Self {
master_password: Vec<u8>,
passwords_file: PathBuf,
passwords: Vec<Password>,
) -> Self {
Self { Self {
master_password, master_password,
passwords_file, passwords_file,
@ -124,18 +120,17 @@ impl Passwords {
.passwords .passwords
.into_iter() .into_iter()
.map(|p| p.encrypt(&self.master_password)) .map(|p| p.encrypt(&self.master_password))
.collect::<LprsResult<Vec<Password>>>()?, .collect::<LprsResult<Vec<Vault>>>()?,
..self ..self
}) })
} }
/// Reload the passwords from the file /// Reload the passwords from the file
pub fn try_reload(passwords_file: PathBuf, master_password: Vec<u8>) -> LprsResult<Self> { pub fn try_reload(passwords_file: PathBuf, master_password: Vec<u8>) -> LprsResult<Self> {
let passwords = let passwords = serde_json::from_str::<Vec<Vault>>(&fs::read_to_string(&passwords_file)?)?
serde_json::from_str::<Vec<Password>>(&fs::read_to_string(&passwords_file)?)? .into_iter()
.into_iter() .map(|p| p.decrypt(master_password.as_slice()))
.map(|p| p.decrypt(master_password.as_slice())) .collect::<LprsResult<Vec<Vault>>>()?;
.collect::<LprsResult<Vec<Password>>>()?;
Ok(Self::new(master_password, passwords_file, passwords)) Ok(Self::new(master_password, passwords_file, passwords))
} }
@ -147,7 +142,7 @@ impl Passwords {
} }
/// Add new password /// Add new password
pub fn add_password(&mut self, password: Password) { pub fn add_password(&mut self, password: Vault) {
self.passwords.push(password) self.passwords.push(password)
} }
} }

View file

@ -18,7 +18,7 @@ use std::{fs, path::Path};
use crate::LprsResult; use crate::LprsResult;
use super::Password; use super::Vault;
/// Return if the password file new file or not /// Return if the password file new file or not
pub fn is_new_password_file(path: &Path) -> LprsResult<bool> { pub fn is_new_password_file(path: &Path) -> LprsResult<bool> {
@ -26,7 +26,7 @@ pub fn is_new_password_file(path: &Path) -> LprsResult<bool> {
let file_content = fs::read_to_string(path)?; let file_content = fs::read_to_string(path)?;
if !file_content.is_empty() if !file_content.is_empty()
&& file_content.trim() != "[]" && file_content.trim() != "[]"
&& serde_json::from_str::<Vec<Password>>(&file_content).is_ok() && serde_json::from_str::<Vec<Vault>>(&file_content).is_ok()
{ {
return Ok(false); return Ok(false);
} }

View file

@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License // 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>. // along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
use crate::{password::Passwords, LprsResult}; use crate::{password::Vaults, LprsResult};
/// Trait to run the command /// Trait to run the command
pub trait RunCommand { pub trait RunCommand {
fn run(&self, password_manager: Passwords) -> LprsResult<()>; fn run(&self, password_manager: Vaults) -> LprsResult<()>;
} }

209
src/vault/mod.rs Normal file
View file

@ -0,0 +1,209 @@
// Lprs - A local CLI vault manager
// Copyright (C) 2024 Awiteb <a@4rs.nl>
//
// 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, marker::PhantomData, path::PathBuf};
use clap::{Parser, ValueEnum};
use serde::{Deserialize, Serialize};
use crate::{LprsError, LprsResult};
use vault_state::*;
pub mod cipher;
mod bitwarden;
mod validator;
pub use bitwarden::*;
pub use validator::*;
#[derive(Clone, Debug, ValueEnum)]
pub enum Format {
Lprs,
BitWarden,
}
/// The states of the vaults
pub mod vault_state {
/// Means the vault is encrypted
#[derive(Clone, Debug, Default)]
pub struct Encrypted;
/// Means the vault is not encrypted
#[derive(Clone, Debug, Default)]
pub struct Plain;
}
/// The vault struct
#[serde_with_macros::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Serialize, Parser)]
pub struct Vault<T>
where
T: std::fmt::Debug + Clone,
{
/// The name of the vault
#[arg(short, long)]
pub name: String,
/// The username
#[arg(short, long)]
pub username: String,
/// The password
#[arg(short, long)]
pub password: String,
/// The service name. e.g the website url
#[arg(short, long)]
pub service: Option<String>,
/// Add a note to the vault
#[arg(short = 'o', long)]
pub note: Option<String>,
/// State phantom
#[serde(skip)]
#[arg(skip)]
phantom: PhantomData<T>,
}
/// The vaults manager
#[derive(Default)]
pub struct Vaults<T>
where
T: std::fmt::Debug + Clone,
{
/// Hash of the master password
pub master_password: Vec<u8>,
/// The json vaults file
pub vaults_file: PathBuf,
/// The vaults
pub vaults: Vec<Vault<T>>,
}
impl<T> Vault<T>
where
T: std::fmt::Debug + Clone,
{
/// Create new [`Vault`] instance
pub fn new(
name: impl Into<String>,
username: impl Into<String>,
password: impl Into<String>,
service: Option<impl Into<String>>,
note: Option<impl Into<String>>,
) -> Self {
Self {
name: name.into(),
username: username.into(),
password: password.into(),
service: service.map(|s| s.into()),
note: note.map(|s| s.into()),
phantom: std::marker::PhantomData,
}
}
}
impl Vault<Encrypted> {
/// Decrypt the vault
pub fn decrypt(&self, master_password: &[u8]) -> LprsResult<Vault<Plain>> {
Ok(Vault::<Plain>::new(
cipher::decrypt(master_password, &self.name)?,
cipher::decrypt(master_password, &self.username)?,
cipher::decrypt(master_password, &self.password)?,
self.service
.as_ref()
.map(|url| cipher::decrypt(master_password, url))
.transpose()?,
self.note
.as_ref()
.map(|note| cipher::decrypt(master_password, note))
.transpose()?,
))
}
}
impl Vault<Plain> {
/// Encrypt the vault
pub fn encrypt(&self, master_password: &[u8]) -> LprsResult<Vault<Encrypted>> {
Ok(Vault::<Encrypted>::new(
cipher::encrypt(master_password, &self.name)?,
cipher::encrypt(master_password, &self.username)?,
cipher::encrypt(master_password, &self.password)?,
self.service
.as_ref()
.map(|url| cipher::encrypt(master_password, url))
.transpose()?,
self.note
.as_ref()
.map(|note| cipher::encrypt(master_password, note))
.transpose()?,
))
}
}
impl<T> Vaults<T>
where
T: std::fmt::Debug + Clone,
{
/// Create new [`Vaults`] instnce
pub fn new(master_password: Vec<u8>, vaults_file: PathBuf, vaults: Vec<Vault<T>>) -> Self {
Self {
master_password,
vaults_file,
vaults,
}
}
}
impl Vaults<Plain> {
/// Encrypt the vaults
pub fn encrypt_vaults(&self) -> LprsResult<Vec<Vault<Encrypted>>> {
self.vaults
.iter()
.map(|p| p.encrypt(&self.master_password))
.collect()
}
/// Reload the vaults from the file then decrypt it
pub fn try_reload(vaults_file: PathBuf, master_password: Vec<u8>) -> LprsResult<Self> {
let vaults =
serde_json::from_str::<Vec<Vault<Encrypted>>>(&fs::read_to_string(&vaults_file)?)?
.into_iter()
.map(|p| p.decrypt(master_password.as_slice()))
.collect::<LprsResult<Vec<Vault<Plain>>>>()?;
Ok(Self::new(master_password, vaults_file, vaults))
}
/// Encrypt the vaults then export it to the file
pub fn try_export(self) -> LprsResult<()> {
fs::write(
&self.vaults_file,
serde_json::to_string(&self.encrypt_vaults()?)?,
)
.map_err(LprsError::Io)
}
/// Add new vault
pub fn add_vault(&mut self, vault: Vault<Plain>) {
self.vaults.push(vault)
}
}
impl ToString for Format {
fn to_string(&self) -> String {
self.to_possible_value()
.expect("There is no skiped values")
.get_name()
.to_owned()
}
}