diff --git a/Cargo.toml b/Cargo.toml index 1850365..5baa308 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "localencrypt" -version = "0.0.3-pre" +version = "0.0.3" authors = ["Anthony Potappel "] edition = "2021" diff --git a/src/api/local_encrypt.rs b/src/api/local_encrypt.rs index 0e306c5..8e2aab7 100644 --- a/src/api/local_encrypt.rs +++ b/src/api/local_encrypt.rs @@ -69,7 +69,7 @@ mod tests { // Using builder to set up LocalEncrypt with localStorage backend let username = "test_builder_local_storage_backend"; let password = "password"; - let credentials = Credentials::new(username, password); + let credentials = Credentials::new(None, username, password); let local_storage = LocalStorage::new(credentials) .await .expect("Failed to create local storage backend"); @@ -101,7 +101,7 @@ mod tests { let local_encrypt2 = LocalEncrypt::new(); assert_eq!(local_encrypt1, local_encrypt2); - let credentials = Credentials::new("username", "password"); + let credentials = Credentials::new(None, "username", "password"); let local_storage = LocalStorage::new(credentials) .await .expect("Failed to create local storage backend"); @@ -118,12 +118,13 @@ mod tests { // Case with password let username = "test_initiate_with_local_storage"; let password = "password"; - let result = StorageBackend::initiate_with_local_storage(username, Some(password)).await; + let result = + StorageBackend::initiate_with_local_storage(None, username, Some(password)).await; assert!(result.is_ok()); // Case without password let username = "username"; - let result = StorageBackend::initiate_with_local_storage(username, None).await; + let result = StorageBackend::initiate_with_local_storage(None, username, None).await; assert!(result.is_ok()); } @@ -132,7 +133,7 @@ mod tests { // Case with Browser backend let username = "test_hard_reset"; let password = "password"; - let backend = StorageBackend::initiate_with_local_storage(username, Some(password)) + let backend = StorageBackend::initiate_with_local_storage(None, username, Some(password)) .await .expect("Failed to initiate local storage backend"); let result = backend.hard_reset().await; diff --git a/src/api/local_encrypt_js.rs b/src/api/local_encrypt_js.rs index 539ea99..fdc6933 100644 --- a/src/api/local_encrypt_js.rs +++ b/src/api/local_encrypt_js.rs @@ -14,7 +14,8 @@ impl LocalEncryptJs { _ = console_log::init_with_level(log::Level::Debug); let future = async move { let storage_backend = - StorageBackend::initiate_with_local_storage(&username, Some(&password)).await?; + StorageBackend::initiate_with_local_storage(None, &username, Some(&password)) + .await?; let local_encrypt = LocalEncrypt::builder() .with_backend(storage_backend) .build(); diff --git a/src/common/credentials.rs b/src/common/credentials.rs index b1f9745..fbe0cd1 100644 --- a/src/common/credentials.rs +++ b/src/common/credentials.rs @@ -1,17 +1,25 @@ +const DEFAULT_ENVIRONMENT: &str = "LocalEncrypt"; + #[derive(Clone, PartialEq)] pub struct Credentials { + environment: String, username: String, password: String, } impl Credentials { - pub fn new(username: &str, password: &str) -> Self { + pub fn new(environment: Option<&str>, username: &str, password: &str) -> Self { Self { + environment: environment.unwrap_or(DEFAULT_ENVIRONMENT).to_string(), username: username.to_string(), password: password.to_string(), } } + pub fn environment(&self) -> String { + self.environment.clone() + } + pub fn username(&self) -> String { self.username.clone() } @@ -26,6 +34,7 @@ use std::fmt::{Debug, Formatter}; impl Debug for Credentials { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("Credentials") + .field("environment", &self.environment) .field("username", &self.username) .field("password", &"********") .finish() @@ -44,7 +53,7 @@ mod tests { async fn test_credentials_new() { let username = "username"; let password = "password"; - let credentials = Credentials::new(username, password); + let credentials = Credentials::new(None, username, password); assert_eq!(credentials.username(), username); assert_eq!(credentials.password(), password); } @@ -53,7 +62,7 @@ mod tests { async fn test_credentials_debug() { let username = "username"; let password = "secretpassword"; - let credentials = Credentials::new(username, password); + let credentials = Credentials::new(None, username, password); let debug_str = format!("{:?}", credentials); assert!(debug_str.contains("username")); assert!(debug_str.contains("********")); diff --git a/src/common/object_key.rs b/src/common/object_key.rs index 5c0a17a..09e1d7b 100644 --- a/src/common/object_key.rs +++ b/src/common/object_key.rs @@ -1,13 +1,16 @@ use crate::SecureStringError; +const DEFAULT_ENVIRONMENT: &str = "LocalEncrypt"; + #[derive(Clone, PartialEq, Debug)] pub struct ObjectKey { + environment: String, tag: String, id: String, } impl ObjectKey { - pub fn new(tag: &str, id: &str) -> Result { + pub fn new(environment: &str, tag: &str, id: &str) -> Result { if id.is_empty() { return Err(SecureStringError::InvalidArgument(String::from( "Tag and ID must not be empty", @@ -15,22 +18,14 @@ impl ObjectKey { } Ok(Self { + environment: environment.to_string(), tag: tag.to_string(), id: id.to_string(), }) } - pub fn new_with_form_tag(id: &str) -> Result { - if id.is_empty() { - return Err(SecureStringError::InvalidArgument(String::from( - "ID must not be empty", - ))); - } - - Ok(Self { - tag: "".to_string(), - id: id.to_string(), - }) + pub fn environment(&self) -> String { + self.environment.clone() } pub fn tag(&self) -> String { @@ -50,22 +45,15 @@ mod tests { #[wasm_bindgen_test] fn test_object_key_new() { - let object_key = ObjectKey::new("test", "test_id").unwrap(); + let object_key = ObjectKey::new("debug", "test", "test_id").unwrap(); assert_eq!(object_key.tag(), "test"); assert_eq!(object_key.id(), "test_id"); } - #[wasm_bindgen_test] - fn test_object_key_new_with_form_tag() { - let object_key = ObjectKey::new_with_form_tag("test_id").unwrap(); - assert_eq!(object_key.tag(), ""); - assert_eq!(object_key.id(), "test_id"); - } - #[wasm_bindgen_test] async fn test_invalid_object_key_creation() { // Check that ObjectKey::new returns an error when given an empty id - let object_key_empty_id = ObjectKey::new("test_tag", ""); + let object_key_empty_id = ObjectKey::new("debug", "test_tag", ""); assert!( object_key_empty_id.is_err(), "Successfully created ObjectKey with empty id" @@ -74,7 +62,7 @@ mod tests { #[wasm_bindgen_test] fn test_object_key_new_empty_tag_and_id() { - let object_key = ObjectKey::new("", ""); + let object_key = ObjectKey::new("debug", "", ""); assert!( object_key.is_err(), "Successfully created ObjectKey with empty tag and id" @@ -89,26 +77,9 @@ mod tests { } } - #[wasm_bindgen_test] - fn test_object_key_new_with_form_tag_empty_id() { - let object_key = ObjectKey::new_with_form_tag(""); - assert!( - object_key.is_err(), - "Successfully created ObjectKey with form tag and empty id" - ); - match object_key { - Err(SecureStringError::InvalidArgument(msg)) => { - assert_eq!(msg, "ID must not be empty"); - } - _ => { - panic!("Unexpected result from ObjectKey::new_with_form_tag with empty id"); - } - } - } - #[wasm_bindgen_test] fn test_object_key_new_error_message() { - let object_key = ObjectKey::new("test", ""); + let object_key = ObjectKey::new("debug", "test", ""); assert!( object_key.is_err(), "Successfully created ObjectKey with empty id" diff --git a/src/crypto/utils.rs b/src/crypto/utils.rs index 4b61b17..b75bb5d 100644 --- a/src/crypto/utils.rs +++ b/src/crypto/utils.rs @@ -69,7 +69,7 @@ mod tests { #[wasm_bindgen_test] async fn test_derive_key_same_password() { - let object_key = ObjectKey::new("crypto_test1", "crypto_test_id1").unwrap(); + let object_key = ObjectKey::new("debug", "crypto_test1", "crypto_test_id1").unwrap(); let password = "password"; let result = derive_key_from_password(&object_key, password).await; @@ -100,7 +100,7 @@ mod tests { #[wasm_bindgen_test] async fn test_derive_key_empty_password() { - let object_key = ObjectKey::new("crypto_test2", "crypto_test_id2").unwrap(); + let object_key = ObjectKey::new("debug", "crypto_test2", "crypto_test_id2").unwrap(); let password_empty = ""; let result_empty = derive_key_from_password(&object_key, password_empty).await; @@ -109,7 +109,7 @@ mod tests { #[wasm_bindgen_test] async fn test_derive_key_different_salt() { - let object_key = ObjectKey::new("crypto_test3", "crypto_test_id3").unwrap(); + let object_key = ObjectKey::new("debug", "crypto_test3", "crypto_test_id3").unwrap(); let password = "password"; // Get the storage_key for saving the salt @@ -138,7 +138,7 @@ mod tests { #[wasm_bindgen_test] async fn test_derive_key_same_salt_different_password() { - let object_key = ObjectKey::new("crypto_test4", "crypto_test_id4").unwrap(); + let object_key = ObjectKey::new("debug", "crypto_test4", "crypto_test_id4").unwrap(); // Derive key with password1 let password1 = "password1"; diff --git a/src/storage/local_storage.rs b/src/storage/local_storage.rs index 9f00736..0d1077d 100644 --- a/src/storage/local_storage.rs +++ b/src/storage/local_storage.rs @@ -25,8 +25,7 @@ impl LocalStorage { let storage = if password.is_empty() { None } else { - let username = credentials.username(); - let user = LocalStorageUser::create_or_validate(&username, &password).await?; + let user = LocalStorageUser::create_or_validate(&credentials).await?; Some(user.secure_storage().clone()) }; Ok(Self { @@ -39,9 +38,9 @@ impl LocalStorage { Ok(self.credentials.username().to_string()) } - pub async fn user_exists(username: &str) -> bool { + pub async fn user_exists(environment: &str, username: &str) -> bool { let hashed_username = hash_username(username); - let object_key = ObjectKey::new(&hashed_username, "self").unwrap(); + let object_key = ObjectKey::new(environment, &hashed_username, "self").unwrap(); SecureStorage::exists(object_key).await } @@ -53,9 +52,10 @@ impl LocalStorage { } pub async fn hard_reset(&self) -> SecureStringResult<()> { + let environment = self.credentials.environment(); let username = self.credentials.username(); let hashed_username = hash_username(&username); - let object_key = ObjectKey::new(&hashed_username, "self")?; + let object_key = ObjectKey::new(&environment, &hashed_username, "self")?; let secure_storage = SecureStorage::for_deletion(object_key); secure_storage.delete().await?; @@ -80,20 +80,20 @@ impl LocalStorage { } } - pub async fn change_password( - &mut self, - new_password: &str, - ) -> SecureStringResult<()> { - + pub async fn change_password(&mut self, new_password: &str) -> SecureStringResult<()> { // copy existing items -- this will be gone after soft_reset let items = self.get_items().await?; - let username = self.credentials.username(); + let new_credentials = Credentials::new( + Some(&self.credentials.environment()), + &self.credentials.username(), + new_password, + ); // cleanup current user self.soft_reset().await?; // create user with same username, but new password - let user = LocalStorageUser::create_or_validate(&username, new_password).await?; + let user = LocalStorageUser::create_or_validate(&new_credentials).await?; self.storage = Some(user.secure_storage().clone()); // put back original items with the new password @@ -102,16 +102,20 @@ impl LocalStorage { } pub async fn initiate_with_password( + environment: Option<&str>, username: &str, password: &str, ) -> SecureStringResult { - let credentials = Credentials::new(username, password); + let credentials = Credentials::new(environment, username, password); Self::new(credentials).await } - pub async fn initiate_with_no_password(username: &str) -> SecureStringResult { + pub async fn initiate_with_no_password( + environment: Option<&str>, + username: &str, + ) -> SecureStringResult { let password = ""; - let credentials = Credentials::new(username, &password); + let credentials = Credentials::new(environment, username, &password); Self::new(credentials).await } @@ -170,7 +174,8 @@ impl LocalStorage { let password = generate_password_base64()?; let derived_key = derive_crypto_key(&password, &item_id).await?; - let object_key = ObjectKey::new("", &item_id)?; + let environment = self.credentials.environment(); + let object_key = ObjectKey::new(&environment, "", &item_id)?; let secure_storage_form = SecureStorage::new(object_key, derived_key); secure_storage_form.save(content).await?; @@ -187,7 +192,8 @@ impl LocalStorage { if let Some(meta) = items.get(item_id) { if let Some(password) = meta.get(PASSWORD_FIELD) { - let object_key = ObjectKey::new("", &item_id)?; + let environment = self.credentials.environment(); + let object_key = ObjectKey::new(&environment, "", &item_id)?; let derived_key = derive_crypto_key(&password, &item_id).await?; let secure_storage_form = SecureStorage::new(object_key, derived_key); @@ -260,7 +266,7 @@ mod tests { #[wasm_bindgen_test] async fn test_list_items() { - let credentials = Credentials::new("test_user_list_items", "password_for_list_items"); + let credentials = Credentials::new(None, "test_user_list_items", "password_for_list_items"); let local_storage = LocalStorage::new(credentials.clone()).await; assert!(local_storage.is_ok()); @@ -280,7 +286,7 @@ mod tests { #[wasm_bindgen_test] async fn test_save_and_load_content() { - let credentials = Credentials::new("test_user_save_load", "password_for_save_load"); + let credentials = Credentials::new(None, "test_user_save_load", "password_for_save_load"); let local_storage = LocalStorage::new(credentials.clone()).await; assert!(local_storage.is_ok()); @@ -329,6 +335,7 @@ mod tests { #[wasm_bindgen_test] async fn test_add_and_delete_items() { let credentials = Credentials::new( + None, "test_user_add_and_delete_items", "password_for_add_and_delete", ); @@ -377,6 +384,7 @@ mod tests { #[wasm_bindgen_test] async fn test_delete_non_existent_item() { let credentials = Credentials::new( + None, "test_user_delete_non_existent_item", "password_for_delete_non_existent", ); @@ -399,6 +407,7 @@ mod tests { #[wasm_bindgen_test] async fn test_load_non_existent_item() { let credentials = Credentials::new( + None, "test_user_load_non_existent_item", "password_for_load_non_existent", ); @@ -420,6 +429,7 @@ mod tests { #[wasm_bindgen_test] async fn test_populate_meta_stored() { let credentials = Credentials::new( + None, "test_user_populate_meta_stored", "password_for_populate_meta_stored", ); @@ -453,14 +463,15 @@ mod tests { // Initialization with correct password let local_storage = - LocalStorage::initiate_with_password("test_user", correct_password).await; + LocalStorage::initiate_with_password(None, "test_user", correct_password).await; assert!( local_storage.is_ok(), "Failed to initialize with correct password" ); // Attempting initialization with incorrect password - let result = LocalStorage::initiate_with_password("test_user", incorrect_password).await; + let result = + LocalStorage::initiate_with_password(None, "test_user", incorrect_password).await; assert!( matches!(result, Err(SecureStringError::DecryptError(_))), "Unexpected error type returned" @@ -481,31 +492,37 @@ mod tests { let test_content = b"test_content"; // Initialization with old password - let local_storage = LocalStorage::initiate_with_password(username, old_password).await; - assert!(local_storage.is_ok(), "Failed to initialize with old password"); + let local_storage = + LocalStorage::initiate_with_password(None, username, old_password).await; + assert!( + local_storage.is_ok(), + "Failed to initialize with old password" + ); let mut local_storage = local_storage.unwrap(); // Add an item - let save_result = local_storage.save_content(item_meta.clone(), test_content).await; - assert!(save_result.is_ok(), "Failed to save content before changing password"); + let save_result = local_storage + .save_content(item_meta.clone(), test_content) + .await; + assert!( + save_result.is_ok(), + "Failed to save content before changing password" + ); // Change password to new password let change_password_result = local_storage.change_password(new_password).await; assert!(change_password_result.is_ok(), "Failed to change password"); // Ensure we can't load with old password - let result = LocalStorage::initiate_with_password(username, old_password).await; + let result = LocalStorage::initiate_with_password(None, username, old_password).await; assert!( matches!(result, Err(SecureStringError::DecryptError(_))), "Unexpectedly succeeded in loading with old password" ); // Ensure we can load with new password - let result = LocalStorage::initiate_with_password(username, new_password).await; - assert!( - result.is_ok(), - "Failed to load with new password" - ); + let result = LocalStorage::initiate_with_password(None, username, new_password).await; + assert!(result.is_ok(), "Failed to load with new password"); let new_local_storage = result.unwrap(); // Ensure our item still exists with new password diff --git a/src/storage/local_storage_ops.rs b/src/storage/local_storage_ops.rs index 1305634..f3555fa 100644 --- a/src/storage/local_storage_ops.rs +++ b/src/storage/local_storage_ops.rs @@ -3,10 +3,13 @@ use web_sys::window; use crate::ObjectKey; -const KEY_PREFIX: &str = "LocalEncrypt"; - pub fn create_storage_key(object_key: &ObjectKey) -> String { - vec![KEY_PREFIX, &object_key.tag(), &object_key.id()].join(":") + vec![ + object_key.environment().as_str(), + object_key.tag().as_str(), + object_key.id().as_str(), + ] + .join(":") } pub async fn save_string(key: &str, value: &str) -> Result<(), JsValue> { @@ -86,8 +89,8 @@ mod tests { #[wasm_bindgen_test] fn test_create_storage_key() { - let object_key = ObjectKey::new("tag", "id").unwrap(); + let object_key = ObjectKey::new("DebugEnv", "tag", "id").unwrap(); let storage_key = create_storage_key(&object_key); - assert_eq!(storage_key, "LocalEncrypt:tag:id"); + assert_eq!(storage_key, "DebugEnv:tag:id"); } } diff --git a/src/storage/local_storage_user.rs b/src/storage/local_storage_user.rs index e53535c..672aa76 100644 --- a/src/storage/local_storage_user.rs +++ b/src/storage/local_storage_user.rs @@ -1,3 +1,4 @@ +use crate::common::Credentials; use crate::crypto::{derive_key_from_password, hash_username}; use crate::{ObjectKey, SecureStorage, SecureStringError, SecureStringResult}; @@ -7,12 +8,14 @@ pub struct LocalStorageUser { } impl LocalStorageUser { - pub async fn create_or_validate(username: &str, password: &str) -> SecureStringResult { - if !LocalStorageUser::exists(username).await { - return LocalStorageUser::create(username, password).await; + pub async fn create_or_validate(credentials: &Credentials) -> SecureStringResult { + let environment = credentials.environment(); + let username = credentials.username(); + if !LocalStorageUser::exists(&environment, &username).await { + return LocalStorageUser::create(credentials).await; } - let user = LocalStorageUser::new(username, password).await?; + let user = LocalStorageUser::new(credentials).await?; // Try to load the passwords to validate the password match user.secure_storage.load().await { @@ -31,13 +34,16 @@ impl LocalStorageUser { } } - async fn new(username: &str, password: &str) -> SecureStringResult { - let hashed_username = hash_username(username); + async fn new(credentials: &Credentials) -> SecureStringResult { + let environment = credentials.environment(); + let username = credentials.username(); + let password = credentials.password(); + let hashed_username = hash_username(&username); - let object_key_user = ObjectKey::new("USER", &hashed_username)?; - let crypto_key = derive_key_from_password(&object_key_user, password).await?; + let object_key_user = ObjectKey::new(&environment, "USER", &hashed_username)?; + let crypto_key = derive_key_from_password(&object_key_user, &password).await?; - let object_key_crypto = ObjectKey::new(&object_key_user.id(), "self")?; + let object_key_crypto = ObjectKey::new(&environment, &object_key_user.id(), "self")?; Ok(Self { secure_storage: SecureStorage::new(object_key_crypto, crypto_key), }) @@ -47,23 +53,25 @@ impl LocalStorageUser { &self.secure_storage } - async fn exists(username: &str) -> bool { + async fn exists(environment: &str, username: &str) -> bool { let hashed_username = hash_username(username); - let object_key = ObjectKey::new(&hashed_username, "self").unwrap(); + let object_key = ObjectKey::new(environment, &hashed_username, "self").unwrap(); SecureStorage::exists(object_key).await } - async fn create(username: &str, password: &str) -> SecureStringResult { + async fn create(credentials: &Credentials) -> SecureStringResult { // this will ensure existing config is deleted - LocalStorageUser::reset(username).await?; - let user = LocalStorageUser::new(username, password).await?; + let environment = credentials.environment(); + let username = credentials.username(); + LocalStorageUser::reset(&environment, &username).await?; + let user = LocalStorageUser::new(credentials).await?; user.secure_storage.empty().await?; Ok(user) } - async fn reset(username: &str) -> SecureStringResult<()> { + async fn reset(environment: &str, username: &str) -> SecureStringResult<()> { let hashed_username = hash_username(username); - let object_key = ObjectKey::new(&hashed_username, "self")?; + let object_key = ObjectKey::new(environment, &hashed_username, "self")?; let secure_storage = SecureStorage::for_deletion(object_key); secure_storage.delete().await?; @@ -84,8 +92,9 @@ mod tests { async fn test_new() { let username = "username"; let password = "password"; + let credentials = Credentials::new(None, username, password); - let user_result = LocalStorageUser::new(username, password).await; + let user_result = LocalStorageUser::new(&credentials).await; assert!(user_result.is_ok()); let user = user_result.unwrap(); @@ -98,13 +107,15 @@ mod tests { #[wasm_bindgen_test] async fn test_create_or_validate_new_user() { + let environment = "debug"; let username = "new_username"; let password = "new_password"; + let credentials = Credentials::new(Some(environment), username.clone(), password.clone()); // Resetting a non-existing user should not return an error - assert!(LocalStorageUser::reset(username).await.is_ok()); + assert!(LocalStorageUser::reset(environment, username).await.is_ok()); - let user_result = LocalStorageUser::create_or_validate(username, password).await; + let user_result = LocalStorageUser::create_or_validate(&credentials).await; assert!(user_result.is_ok()); let user = user_result.unwrap(); @@ -117,15 +128,18 @@ mod tests { #[wasm_bindgen_test] async fn test_create_or_validate_existing_user_wrong_password() { + let environment = "debug"; let username = "existing_username"; let password = "correct_password"; - let wrong_password = "wrong_password"; + let credentials = Credentials::new(Some(environment), username.clone(), password.clone()); + let wrong_credentials = + Credentials::new(Some(environment), username.clone(), "wrong_password"); // Create the user - LocalStorageUser::create(username, password).await.unwrap(); + LocalStorageUser::create(&credentials).await.unwrap(); // Now try to validate the user with wrong password - let user_result = LocalStorageUser::create_or_validate(username, wrong_password).await; + let user_result = LocalStorageUser::create_or_validate(&wrong_credentials).await; assert!(user_result.is_err()); assert_eq!( user_result.unwrap_err(), @@ -137,12 +151,13 @@ mod tests { async fn test_create_or_validate_existing_user_correct_password() { let username = "existing_username_2"; let password = "correct_password_2"; + let credentials = Credentials::new(None, username.clone(), password.clone()); // Create the user - LocalStorageUser::create(username, password).await.unwrap(); + LocalStorageUser::create(&credentials).await.unwrap(); // Now try to validate the user with correct password - let user_result = LocalStorageUser::create_or_validate(username, password).await; + let user_result = LocalStorageUser::create_or_validate(&credentials).await; assert!(user_result.is_ok()); let user = user_result.unwrap(); @@ -155,34 +170,40 @@ mod tests { #[wasm_bindgen_test] async fn test_exists() { + let environment = "debug"; let username = "username_for_exists_test"; let password = "password_for_exists_test"; + let credentials = Credentials::new(Some(environment), username, password); // Assert user doesn't exist initially - assert_eq!(LocalStorageUser::exists(username).await, false); + assert_eq!(LocalStorageUser::exists(environment, username).await, false); // Create the user - LocalStorageUser::create(username, password).await.unwrap(); + LocalStorageUser::create(&credentials).await.unwrap(); // Assert user now exists - assert_eq!(LocalStorageUser::exists(username).await, true); + assert_eq!(LocalStorageUser::exists(environment, username).await, true); } #[wasm_bindgen_test] async fn test_reset() { + let environment = "debug"; let username = "username_for_reset_test"; let password = "password_for_reset_test"; + let credentials = Credentials::new(Some(environment), username.clone(), password.clone()); // Create the user - LocalStorageUser::create(username, password).await.unwrap(); + LocalStorageUser::create(&credentials).await.unwrap(); // Assert user now exists - assert_eq!(LocalStorageUser::exists(username).await, true); + assert_eq!(LocalStorageUser::exists(environment, username).await, true); // Reset the user - LocalStorageUser::reset(username).await.unwrap(); + LocalStorageUser::reset(environment, username) + .await + .unwrap(); // Assert user doesn't exist now - assert_eq!(LocalStorageUser::exists(username).await, false); + assert_eq!(LocalStorageUser::exists(environment, username).await, false); } } diff --git a/src/storage/secure_storage.rs b/src/storage/secure_storage.rs index b5ede09..77d5ce1 100644 --- a/src/storage/secure_storage.rs +++ b/src/storage/secure_storage.rs @@ -116,7 +116,7 @@ mod tests { #[wasm_bindgen_test] async fn test_new() { - let object_key = ObjectKey::new("test1", "test_id1").unwrap(); + let object_key = ObjectKey::new("debug", "test1", "test_id1").unwrap(); let password = "password_for_new"; let crypto_key_result = derive_key_from_password(&object_key, password).await; @@ -130,7 +130,7 @@ mod tests { #[wasm_bindgen_test] fn test_for_deletion() { - let object_key = ObjectKey::new("test2", "test_id2").unwrap(); + let object_key = ObjectKey::new("debug", "test2", "test_id2").unwrap(); let secure_storage = SecureStorage::for_deletion(object_key.clone()); assert_eq!(secure_storage.object_key, object_key); @@ -139,7 +139,7 @@ mod tests { #[wasm_bindgen_test] async fn test_save_load_delete() { - let object_key = ObjectKey::new("test3", "test_id3").unwrap(); + let object_key = ObjectKey::new("debug", "test3", "test_id3").unwrap(); let password = "password_for_save_load_delete"; let value = "test_value_for_save_load_delete"; @@ -177,7 +177,7 @@ mod tests { #[wasm_bindgen_test] async fn test_exists() { - let object_key = ObjectKey::new("test_exists", "test_id_exists").unwrap(); + let object_key = ObjectKey::new("debug", "test_exists", "test_id_exists").unwrap(); let password = "password_for_exists"; let value = "test_value_for_exists"; @@ -211,7 +211,7 @@ mod tests { #[wasm_bindgen_test] async fn test_empty() { - let object_key = ObjectKey::new("test_empty", "test_id_empty").unwrap(); + let object_key = ObjectKey::new("debug", "test_empty", "test_id_empty").unwrap(); let password = "password_for_empty"; let value = "test_value_for_empty"; @@ -241,7 +241,8 @@ mod tests { #[wasm_bindgen_test] async fn test_for_deletion_no_key() { - let object_key = ObjectKey::new("test_for_deletion", "test_id_for_deletion").unwrap(); + let object_key = + ObjectKey::new("debug", "test_for_deletion", "test_id_for_deletion").unwrap(); let secure_storage = SecureStorage::for_deletion(object_key.clone()); @@ -269,7 +270,8 @@ mod tests { #[wasm_bindgen_test] async fn test_large_string() { - let object_key = ObjectKey::new("test_large_string", "test_id_large_string").unwrap(); + let object_key = + ObjectKey::new("debug", "test_large_string", "test_id_large_string").unwrap(); let password = "password"; let value = "a".repeat(1_000_000); @@ -295,8 +297,12 @@ mod tests { #[wasm_bindgen_test] async fn test_different_crypto_key() { - let object_key = - ObjectKey::new("test_different_crypto_key", "test_id_different_crypto_key").unwrap(); + let object_key = ObjectKey::new( + "debug", + "test_different_crypto_key", + "test_id_different_crypto_key", + ) + .unwrap(); let password = "password"; let value = "test_value"; @@ -332,6 +338,7 @@ mod tests { #[wasm_bindgen_test] async fn test_special_characters_in_object_key() { let object_key = ObjectKey::new( + "debug", "test_@$%^&*(){}[];:/?,<>'\"\\", "test_id_@$%^&*(){}[];:/?,<>'\"\\", ) diff --git a/src/storage/storage_backend.rs b/src/storage/storage_backend.rs index c27944e..0089ebc 100644 --- a/src/storage/storage_backend.rs +++ b/src/storage/storage_backend.rs @@ -22,12 +22,15 @@ impl BrowserStorage { impl StorageBackend { pub async fn initiate_with_local_storage( + environment: Option<&str>, username: &str, password: Option<&str>, ) -> SecureStringResult { let local_storage = match password { - Some(password) => LocalStorage::initiate_with_password(username, password).await?, - None => LocalStorage::initiate_with_no_password(username).await?, + Some(password) => { + LocalStorage::initiate_with_password(environment, username, password).await? + } + None => LocalStorage::initiate_with_no_password(environment, username).await?, }; Ok(Self::Browser(BrowserStorage::LocalStorage(local_storage))) @@ -53,16 +56,21 @@ impl StorageBackend { pub async fn change_password( &self, + environment: Option<&str>, old_password: &str, new_password: &str, ) -> SecureStringResult<()> { match self { StorageBackend::Browser(BrowserStorage::LocalStorage(local_storage)) => { // get the username from the existing local_storage - let username = local_storage.get_username().expect("Failed to get username"); + let username = local_storage + .get_username() + .expect("Failed to get username"); // create a new LocalStorage instance with the old password - let mut old_local_storage = LocalStorage::initiate_with_password(&username, old_password).await?; + let mut old_local_storage = + LocalStorage::initiate_with_password(environment, &username, old_password) + .await?; // perform the change password operation old_local_storage.change_password(new_password).await?; @@ -87,12 +95,13 @@ mod tests { // Case with password let username = "test_initiate_with_local_storage"; let password = "password"; - let result = StorageBackend::initiate_with_local_storage(username, Some(password)).await; + let result = + StorageBackend::initiate_with_local_storage(None, username, Some(password)).await; assert!(result.is_ok()); // Case without password let username = "username"; - let result = StorageBackend::initiate_with_local_storage(username, None).await; + let result = StorageBackend::initiate_with_local_storage(None, username, None).await; assert!(result.is_ok()); } @@ -101,7 +110,7 @@ mod tests { // Case with Browser backend let username = "test_hard_reset"; let password = "password"; - let backend = StorageBackend::initiate_with_local_storage(username, Some(password)) + let backend = StorageBackend::initiate_with_local_storage(None, username, Some(password)) .await .expect("Failed to initiate local storage backend"); let result = backend.hard_reset().await; @@ -119,7 +128,7 @@ mod tests { // Case with Browser backend let username = "test_validate_password"; let password = "password"; - let backend = StorageBackend::initiate_with_local_storage(username, Some(password)) + let backend = StorageBackend::initiate_with_local_storage(None, username, Some(password)) .await .expect("Failed to initiate local storage backend"); let result = backend.validate_password().await; @@ -139,23 +148,35 @@ mod tests { let username = "test_change_password_local_storage"; let password = "old_password"; let new_password = "new_password"; - let mut backend = StorageBackend::initiate_with_local_storage(username, Some(password)) - .await - .expect("Failed to initiate local storage backend"); + let mut backend = + StorageBackend::initiate_with_local_storage(None, username, Some(password)) + .await + .expect("Failed to initiate local storage backend"); // Get LocalStorage and add some items to it match &mut backend { StorageBackend::Browser(BrowserStorage::LocalStorage(local_storage)) => { // Add some items to local_storage - let item_meta = ItemMetaData::new_with_tags("item1", vec![("tag1".to_string(), "value1".to_string()), ("tag2".to_string(), "value2".to_string())].into_iter().collect()); + let item_meta = ItemMetaData::new_with_tags( + "item1", + vec![ + ("tag1".to_string(), "value1".to_string()), + ("tag2".to_string(), "value2".to_string()), + ] + .into_iter() + .collect(), + ); let content = b"Content of the item"; - local_storage.save_content(item_meta, content).await.expect("Failed to add item"); + local_storage + .save_content(item_meta, content) + .await + .expect("Failed to add item"); } _ => panic!("Expected browser local storage backend"), } // Change password - let result = backend.change_password(password, new_password).await; + let result = backend.change_password(None, password, new_password).await; assert!(result.is_ok()); // Validate should fail on old backend @@ -164,21 +185,38 @@ mod tests { assert_eq!(result.unwrap(), false); // password should no longer be correct // Get backend with new password - let new_backend = StorageBackend::initiate_with_local_storage(username, Some(new_password)) - .await - .expect("Failed to initiate local storage backend"); + let new_backend = + StorageBackend::initiate_with_local_storage(None, username, Some(new_password)) + .await + .expect("Failed to initiate local storage backend"); // Get LocalStorage and check that the items are still there match new_backend { StorageBackend::Browser(BrowserStorage::LocalStorage(new_local_storage)) => { // Check that the items are still there - let items = new_local_storage.list_items().await.expect("Failed to list items"); + let items = new_local_storage + .list_items() + .await + .expect("Failed to list items"); assert_eq!(items.len(), 1); // one item was added assert_eq!(items[0].id(), "item1"); - assert_eq!(items[0].tags(), Some(vec![("tag1".to_string(), "value1".to_string()), ("tag2".to_string(), "value2".to_string())].into_iter().collect())); + assert_eq!( + items[0].tags(), + Some( + vec![ + ("tag1".to_string(), "value1".to_string()), + ("tag2".to_string(), "value2".to_string()) + ] + .into_iter() + .collect() + ) + ); // Check the content of the item - let content = new_local_storage.load_content("item1").await.expect("Failed to load content"); + let content = new_local_storage + .load_content("item1") + .await + .expect("Failed to load content"); assert_eq!(content.as_ref().unwrap().as_slice(), b"Content of the item"); } _ => panic!("Expected browser local storage backend"),