-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch to object-driven approach for profiles
- Loading branch information
1 parent
0310a99
commit ee93433
Showing
12 changed files
with
323 additions
and
313 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,22 @@ | ||
//! Contains functions related to key manipulation | ||
//! Contains wrapper functions above profiles to get and set current profile's key | ||
use crate::{Error, Result, Key}; | ||
use super::profile; | ||
use crate::log_debug; | ||
use crate::core::encryption::cipher; | ||
use super::profiles; | ||
use crate::{Key, Result}; | ||
|
||
/// Gets the key for the current profile | ||
pub fn get_key() -> Result<Key> { | ||
log_debug!("Getting encryption key from current profile"); | ||
|
||
let profile_data = profiles::get_current_profile()?; | ||
Ok(profile_data.key) | ||
let mut profiles = profile::get_profiles(); | ||
let profile = profiles.get_current_profile()?; | ||
Ok(profile.key) | ||
} | ||
|
||
/// Sets the key for the current profile | ||
pub fn set_key(new_key: Key) -> Result<()> { | ||
log_debug!("Setting a new encryption key for current profile"); | ||
|
||
let mut profile = profiles::get_current_profile()?; | ||
profile.key = new_key; | ||
|
||
profiles::save_profile(profile) | ||
.map_err(|err| Error::IOError(format!("Unable to save profile data: {}", err))) | ||
} | ||
|
||
/// Generates new key and overwrites the old for the current profile | ||
pub fn generate_new_key() -> Result<()> { | ||
log_debug!("Generating new encryption key for current profile"); | ||
|
||
let mut profile = profiles::get_current_profile()?; | ||
profile.key = cipher::generate_key(); | ||
|
||
profiles::save_profile(profile) | ||
.map_err(|err| Error::IOError(format!("Unable to save profile data: {}", err))) | ||
let mut profiles = profile::get_profiles(); | ||
let profile = profiles.get_current_profile()?; | ||
profile.set_key(new_key); | ||
profiles.save() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
//! Contains function for user profile manipulation | ||
use super::auth; | ||
use super::io::{read_file, write_file}; | ||
use crate::core::data::os; | ||
use crate::core::encryption::cipher; | ||
use crate::{log_debug, log_error, log_info, Error, Key, Result}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::io::{self}; | ||
use std::path::PathBuf; | ||
|
||
/// Name of the file which stores all the profile data | ||
const PROFILES_FILE_NAME: &str = "profiles.json"; | ||
|
||
/// Struct holding all the needed profile information for the program. Saved on the disk as a JSON | ||
/// file | ||
#[derive(Serialize, Deserialize, Debug)] | ||
pub struct LockboxProfiles { | ||
current_profile: Option<String>, | ||
profiles: Vec<Profile>, | ||
#[serde(skip)] | ||
file_path: PathBuf | ||
} | ||
|
||
/// Object-driven approach | ||
impl LockboxProfiles { | ||
/// Imports self from the stored "profiles.json" file. In case of the file missing, generates a | ||
/// new object with default empty values | ||
pub fn import() -> Result<Self> { | ||
log_debug!("Importing Lockbox profiles"); | ||
let data_directory = os::get_data_dir()?; | ||
let profiles_file = data_directory.join(PROFILES_FILE_NAME); | ||
|
||
let profiles = match read_file(&profiles_file) { | ||
Ok(file_data) => { | ||
let mut profiles: LockboxProfiles = serde_json::from_str(&file_data)?; | ||
profiles.file_path = profiles_file; | ||
profiles | ||
}, | ||
Err(err) => { | ||
if err.kind() == io::ErrorKind::NotFound { | ||
log_info!("\"profiles.json\" file doesn't exist. Generating new profiles data"); | ||
Self::new(profiles_file) | ||
} else { | ||
return Err(err.into()); | ||
} | ||
} | ||
}; | ||
|
||
Ok(profiles) | ||
} | ||
|
||
/// Bare-minimum constructor to use in case of file not being available to import from | ||
fn new( | ||
file_path: PathBuf | ||
) -> Self { | ||
LockboxProfiles { | ||
current_profile: None, | ||
profiles: vec![], | ||
file_path | ||
} | ||
} | ||
|
||
/// Returns currently selected profile data | ||
pub fn get_current_profile(&mut self) -> Result<&mut Profile> { | ||
log_debug!("Getting current profile"); | ||
let current_profile = self.current_profile.clone(); | ||
|
||
let profile = match current_profile { | ||
None => return Err(Error::ProfileError("No profile is currently selected".to_string())), | ||
Some(profile_name) => { | ||
self.find_profile(&profile_name)? | ||
} | ||
}; | ||
|
||
Ok(profile) | ||
} | ||
|
||
/// Returns a list of currently available profiles | ||
pub fn get_profiles(&self) -> &Vec<Profile> { | ||
log_debug!("Getting all available profiles"); | ||
&self.profiles | ||
} | ||
|
||
/// Sets the current profile to profile which name was supplied. Returns an error if given | ||
/// profile doesn't exist | ||
pub fn set_current(&mut self, profile_name: &str) -> Result<()> { | ||
log_debug!("Setting current profile to \"{}\"", profile_name); | ||
|
||
self.current_profile = Some(profile_name.to_string()); | ||
self.save()?; | ||
|
||
log_debug!("Set current profile to \"{}\"", profile_name); | ||
Ok(()) | ||
} | ||
|
||
/// Deletes a profile with provided name | ||
pub fn delete_profile(&mut self, profile_name: &str) -> Result<()> { | ||
log_debug!("Deleting profile with name \"{}\"", profile_name); | ||
|
||
for (i, profile) in self.profiles.iter().enumerate() { | ||
if profile.name == profile_name { | ||
self.profiles.remove(i); | ||
self.current_profile = { | ||
if self.profiles.is_empty() { | ||
None | ||
} else { | ||
Some(self.profiles.get(0).unwrap().name.clone()) | ||
} | ||
}; | ||
self.save()?; | ||
return Ok(()) | ||
} | ||
} | ||
|
||
Err(Error::ProfileError(format!("Profile with name \"{}\" doesn\'t exist", profile_name))) | ||
} | ||
|
||
/// Saves provided profile data to profiles file. Updates existing profile or creates a new one, | ||
/// if it doesn't already exist | ||
#[allow(dead_code)] | ||
pub fn save_profile(&mut self, profile: Profile) -> Result<()> { | ||
log_debug!("Saving profile: {:?}", &profile); | ||
|
||
let profile_name = profile.name.clone(); | ||
|
||
if self.profiles.is_empty() { | ||
self.profiles.push(profile); | ||
self.current_profile = Some(profile_name); | ||
} else { | ||
for i in 0..self.profiles.len() { | ||
if self.profiles[i].name == profile_name { | ||
self.profiles.insert(i, profile); | ||
break; | ||
} | ||
|
||
if i == self.profiles.len() - 1 { | ||
self.profiles.push(profile); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
self.save()?; | ||
Ok(()) | ||
} | ||
|
||
/// Adds a new profile to the profiles file. Errors if the profile already exists, as this | ||
/// functions only accepts new profiles | ||
pub fn new_profile(&mut self, profile: Profile) -> Result<()> { | ||
log_debug!("Adding a new profile: {:?}", &profile); | ||
|
||
let profile_name = profile.name.clone(); | ||
if self.find_profile(&profile_name).is_ok() { | ||
return Err(Error::ProfileError(format!("Profile with name \"{}\" already exists", profile_name))); | ||
} | ||
self.profiles.push(profile); | ||
|
||
self.save()?; | ||
Ok(()) | ||
} | ||
|
||
/// Returns profile for profile which name was supplied | ||
pub fn find_profile(&mut self, profile_name: &str) -> Result<&mut Profile> { | ||
log_debug!("Searching for profile with name \"{}\"", profile_name); | ||
|
||
for profile in &mut self.profiles { | ||
if profile.name == profile_name { | ||
return Ok(profile) | ||
} | ||
} | ||
|
||
Err(Error::ProfileError(format!("Profile with name \"{}\" doesn\'t exist", profile_name))) | ||
} | ||
|
||
/// Writes to the profile data file. Overwrites old data | ||
pub fn save(&self) -> Result<()> { | ||
log_debug!("Saving profiles data to \"profiles.json\""); | ||
let json_data = serde_json::to_string(&self)?; | ||
|
||
write_file(&self.file_path, &json_data, true)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
/// Struct containing main information about a profile | ||
#[derive(Serialize, Deserialize, Debug)] | ||
pub struct Profile { | ||
pub name: String, | ||
pub key: Key, | ||
password_hash: String | ||
} | ||
|
||
impl Profile { | ||
pub fn new( | ||
name: &str, | ||
password: &str | ||
) -> Result<Self> { | ||
let (hash, _salt) = auth::hash_password(password)?; | ||
Ok(Profile { | ||
name: name.to_string(), | ||
key: cipher::generate_key(), | ||
password_hash: hash, | ||
}) | ||
} | ||
|
||
/// Checks whether the provided password is valid for the profile | ||
pub fn verify_password(&self, password: &str) -> bool { | ||
auth::verify_password(&self.password_hash, password) | ||
} | ||
|
||
/// Sets a new key for the profile | ||
pub fn set_key(&mut self, key: Key) { | ||
self.key = key; | ||
} | ||
} | ||
|
||
pub fn get_profiles() -> LockboxProfiles { | ||
log_debug!("Getting Lockbox profiles"); | ||
|
||
match LockboxProfiles::import() { | ||
Ok(profiles) => profiles, | ||
Err(err) => { | ||
log_error!("Unable to import Lockbox profiles"); | ||
log_error!("{}", err); | ||
std::process::exit(1); | ||
} | ||
} | ||
} |
Oops, something went wrong.