diff --git a/Cargo.lock b/Cargo.lock index b73ca6026..01d4d08cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -675,6 +675,7 @@ dependencies = [ "bitwarden-core", "bitwarden-crypto", "bitwarden-error", + "bitwarden-uuid", "chrono", "data-encoding", "hmac", diff --git a/bitwarden_license/bitwarden-sm/src/projects/create.rs b/bitwarden_license/bitwarden-sm/src/projects/create.rs index a5abdd2a8..5b7686882 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/create.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/create.rs @@ -1,5 +1,5 @@ use bitwarden_api_api::models::ProjectCreateRequestModel; -use bitwarden_core::{key_management::SymmetricKeyId, Client}; +use bitwarden_core::{key_management::SymmetricKeyId, Client, OrganizationId}; use bitwarden_crypto::Encryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -28,7 +28,7 @@ pub(crate) async fn create_project( input.validate()?; let key_store = client.internal.get_key_store(); - let key = SymmetricKeyId::Organization(input.organization_id); + let key = SymmetricKeyId::Organization(OrganizationId::new(input.organization_id)); let project = Some(ProjectCreateRequestModel { name: input diff --git a/bitwarden_license/bitwarden-sm/src/projects/project_response.rs b/bitwarden_license/bitwarden-sm/src/projects/project_response.rs index 2484f2ef7..4d37c963f 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/project_response.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/project_response.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::ProjectResponseModel; use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, - require, + require, OrganizationId, }; use bitwarden_crypto::{Decryptable, EncString, KeyStoreContext}; use chrono::{DateTime, Utc}; @@ -28,7 +28,7 @@ impl ProjectResponse { ctx: &mut KeyStoreContext, ) -> Result { let organization_id = require!(response.organization_id); - let key = SymmetricKeyId::Organization(organization_id); + let key = SymmetricKeyId::Organization(OrganizationId::new(organization_id)); let name = require!(response.name) .parse::()? diff --git a/bitwarden_license/bitwarden-sm/src/projects/update.rs b/bitwarden_license/bitwarden-sm/src/projects/update.rs index 8f89e318b..da9c21812 100644 --- a/bitwarden_license/bitwarden-sm/src/projects/update.rs +++ b/bitwarden_license/bitwarden-sm/src/projects/update.rs @@ -1,5 +1,5 @@ use bitwarden_api_api::models::ProjectUpdateRequestModel; -use bitwarden_core::{key_management::SymmetricKeyId, Client}; +use bitwarden_core::{key_management::SymmetricKeyId, Client, OrganizationId}; use bitwarden_crypto::Encryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -30,7 +30,7 @@ pub(crate) async fn update_project( input.validate()?; let key_store = client.internal.get_key_store(); - let key = SymmetricKeyId::Organization(input.organization_id); + let key = SymmetricKeyId::Organization(OrganizationId::new(input.organization_id)); let project = Some(ProjectUpdateRequestModel { name: input diff --git a/bitwarden_license/bitwarden-sm/src/secrets/create.rs b/bitwarden_license/bitwarden-sm/src/secrets/create.rs index c554c9d60..d3ed304c0 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/create.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/create.rs @@ -1,5 +1,5 @@ use bitwarden_api_api::models::SecretCreateRequestModel; -use bitwarden_core::{key_management::SymmetricKeyId, Client}; +use bitwarden_core::{key_management::SymmetricKeyId, Client, OrganizationId}; use bitwarden_crypto::Encryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -36,7 +36,7 @@ pub(crate) async fn create_secret( input.validate()?; let key_store = client.internal.get_key_store(); - let key = SymmetricKeyId::Organization(input.organization_id); + let key = SymmetricKeyId::Organization(OrganizationId::new(input.organization_id)); let secret = { let mut ctx = key_store.context(); diff --git a/bitwarden_license/bitwarden-sm/src/secrets/list.rs b/bitwarden_license/bitwarden-sm/src/secrets/list.rs index 1cbaa73f2..8b2090327 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/list.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/list.rs @@ -4,7 +4,7 @@ use bitwarden_api_api::models::{ use bitwarden_core::{ client::Client, key_management::{KeyIds, SymmetricKeyId}, - require, + require, OrganizationId, }; use bitwarden_crypto::{Decryptable, EncString, KeyStoreContext}; use schemars::JsonSchema; @@ -99,7 +99,7 @@ impl SecretIdentifierResponse { ctx: &mut KeyStoreContext, ) -> Result { let organization_id = require!(response.organization_id); - let enc_key = SymmetricKeyId::Organization(organization_id); + let enc_key = SymmetricKeyId::Organization(OrganizationId::new(organization_id)); let key = require!(response.key) .parse::()? diff --git a/bitwarden_license/bitwarden-sm/src/secrets/secret_response.rs b/bitwarden_license/bitwarden-sm/src/secrets/secret_response.rs index f991a0c05..040b8c6dc 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/secret_response.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/secret_response.rs @@ -3,7 +3,7 @@ use bitwarden_api_api::models::{ }; use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, - require, + require, OrganizationId, }; use bitwarden_crypto::{Decryptable, EncString, KeyStoreContext}; use chrono::{DateTime, Utc}; @@ -52,7 +52,7 @@ impl SecretResponse { ctx: &mut KeyStoreContext, ) -> Result { let organization_id = require!(response.organization_id); - let enc_key = SymmetricKeyId::Organization(organization_id); + let enc_key = SymmetricKeyId::Organization(OrganizationId::new(organization_id)); let key = require!(response.key) .parse::()? diff --git a/bitwarden_license/bitwarden-sm/src/secrets/update.rs b/bitwarden_license/bitwarden-sm/src/secrets/update.rs index 0f1004724..7a970937a 100644 --- a/bitwarden_license/bitwarden-sm/src/secrets/update.rs +++ b/bitwarden_license/bitwarden-sm/src/secrets/update.rs @@ -1,5 +1,5 @@ use bitwarden_api_api::models::SecretUpdateRequestModel; -use bitwarden_core::{key_management::SymmetricKeyId, Client}; +use bitwarden_core::{key_management::SymmetricKeyId, Client, OrganizationId}; use bitwarden_crypto::Encryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -35,7 +35,7 @@ pub(crate) async fn update_secret( input.validate()?; let key_store = client.internal.get_key_store(); - let key = SymmetricKeyId::Organization(input.organization_id); + let key = SymmetricKeyId::Organization(OrganizationId::new(input.organization_id)); let secret = { let mut ctx = key_store.context(); diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 5655f2110..c6e0f082b 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -138,9 +138,12 @@ mod tests { use bitwarden_crypto::{Kdf, MasterKey}; use super::*; - use crate::key_management::{ - crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest}, - SymmetricKeyId, + use crate::{ + key_management::{ + crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest}, + SymmetricKeyId, + }, + UserId, }; #[test] @@ -241,7 +244,7 @@ mod tests { new_device .crypto() .initialize_user_crypto(InitUserCryptoRequest { - user_id: Some(uuid::Uuid::new_v4()), + user_id: Some(UserId::new_v4()), kdf_params: kdf, email: email.to_owned(), private_key, diff --git a/crates/bitwarden-core/src/auth/login/access_token.rs b/crates/bitwarden-core/src/auth/login/access_token.rs index 1563bc10b..9227273df 100644 --- a/crates/bitwarden-core/src/auth/login/access_token.rs +++ b/crates/bitwarden-core/src/auth/login/access_token.rs @@ -5,7 +5,6 @@ use bitwarden_crypto::{EncString, KeyDecryptable, SymmetricCryptoKey}; use chrono::Utc; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use uuid::Uuid; use super::LoginError; use crate::{ @@ -17,7 +16,7 @@ use crate::{ client::{LoginMethod, ServiceAccountLoginMethod}, require, secrets_manager::state::{self, ClientState}, - Client, + Client, OrganizationId, }; pub(crate) async fn login_access_token( @@ -118,7 +117,7 @@ fn load_tokens_from_state( client: &Client, state_file: &Path, access_token: &AccessToken, -) -> Result { +) -> Result { let client_state = state::get(state_file, access_token)?; let token: JwtToken = client_state.token.parse()?; @@ -127,7 +126,7 @@ fn load_tokens_from_state( let time_till_expiration = (token.exp as i64) - Utc::now().timestamp(); if time_till_expiration > 0 { - let organization_id: Uuid = organization_id + let organization_id: OrganizationId = organization_id .parse() .map_err(|_| LoginError::InvalidOrganizationId)?; let encryption_key = SymmetricCryptoKey::try_from(client_state.encryption_key)?; diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index ab8d84cd4..235606df7 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -4,11 +4,11 @@ use bitwarden_crypto::{EncString, UnsignedSharedKey}; use bitwarden_crypto::{KeyStore, SymmetricCryptoKey}; use bitwarden_error::bitwarden_error; use thiserror::Error; -#[cfg(any(feature = "internal", feature = "secrets"))] -use uuid::Uuid; #[cfg(any(feature = "internal", feature = "secrets"))] use crate::key_management::{KeyIds, SymmetricKeyId}; +#[cfg(any(feature = "secrets", feature = "internal"))] +use crate::OrganizationId; use crate::{error::UserIdAlreadySetError, MissingPrivateKeyError, VaultLockedError}; #[allow(missing_docs)] @@ -100,7 +100,7 @@ impl EncryptionSettings { /// This is used only for logging in Secrets Manager with an access token #[cfg(feature = "secrets")] pub(crate) fn new_single_org_key( - organization_id: Uuid, + organization_id: OrganizationId, key: SymmetricCryptoKey, store: &KeyStore, ) { @@ -114,7 +114,7 @@ impl EncryptionSettings { #[cfg(feature = "internal")] pub(crate) fn set_org_keys( - org_enc_keys: Vec<(Uuid, UnsignedSharedKey)>, + org_enc_keys: Vec<(OrganizationId, UnsignedSharedKey)>, store: &KeyStore, ) -> Result<(), EncryptionSettingsError> { use crate::key_management::AsymmetricKeyId; diff --git a/crates/bitwarden-core/src/client/internal.rs b/crates/bitwarden-core/src/client/internal.rs index 8749a4d32..7b9d93f6c 100644 --- a/crates/bitwarden-core/src/client/internal.rs +++ b/crates/bitwarden-core/src/client/internal.rs @@ -6,20 +6,20 @@ use bitwarden_crypto::SymmetricCryptoKey; #[cfg(feature = "internal")] use bitwarden_crypto::{EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey}; use chrono::Utc; -use uuid::Uuid; use super::encryption_settings::EncryptionSettings; #[cfg(feature = "secrets")] use super::login_method::ServiceAccountLoginMethod; use crate::{ auth::renew::renew_token, client::login_method::LoginMethod, error::UserIdAlreadySetError, - key_management::KeyIds, DeviceType, + key_management::KeyIds, DeviceType, UserId, }; #[cfg(feature = "internal")] use crate::{ client::encryption_settings::EncryptionSettingsError, client::{flags::Flags, login_method::UserLoginMethod}, error::NotAuthenticatedError, + OrganizationId, }; #[allow(missing_docs)] @@ -45,7 +45,7 @@ pub(crate) struct Tokens { #[allow(missing_docs)] #[derive(Debug)] pub struct InternalClient { - pub(crate) user_id: OnceLock, + pub(crate) user_id: OnceLock, pub(crate) tokens: RwLock, pub(crate) login_method: RwLock>>, @@ -86,7 +86,7 @@ impl InternalClient { } #[allow(missing_docs)] - pub fn get_access_token_organization(&self) -> Option { + pub fn get_access_token_organization(&self) -> Option { match self .login_method .read() @@ -183,12 +183,12 @@ impl InternalClient { } #[allow(missing_docs)] - pub fn init_user_id(&self, user_id: Uuid) -> Result<(), UserIdAlreadySetError> { + pub fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> { self.user_id.set(user_id).map_err(|_| UserIdAlreadySetError) } #[allow(missing_docs)] - pub fn get_user_id(&self) -> Option { + pub fn get_user_id(&self) -> Option { self.user_id.get().copied() } @@ -233,7 +233,7 @@ impl InternalClient { #[cfg(feature = "secrets")] pub(crate) fn initialize_crypto_single_org_key( &self, - organization_id: Uuid, + organization_id: OrganizationId, key: SymmetricCryptoKey, ) { EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store); @@ -243,7 +243,7 @@ impl InternalClient { #[cfg(feature = "internal")] pub fn initialize_org_crypto( &self, - org_keys: Vec<(Uuid, UnsignedSharedKey)>, + org_keys: Vec<(OrganizationId, UnsignedSharedKey)>, ) -> Result<(), EncryptionSettingsError> { EncryptionSettings::set_org_keys(org_keys, &self.key_store) } diff --git a/crates/bitwarden-core/src/client/login_method.rs b/crates/bitwarden-core/src/client/login_method.rs index 67db15a71..f412976fa 100644 --- a/crates/bitwarden-core/src/client/login_method.rs +++ b/crates/bitwarden-core/src/client/login_method.rs @@ -2,11 +2,9 @@ use std::path::PathBuf; use bitwarden_crypto::Kdf; -#[cfg(feature = "secrets")] -use uuid::Uuid; #[cfg(feature = "secrets")] -use crate::auth::AccessToken; +use crate::{auth::AccessToken, OrganizationId}; #[derive(Debug)] pub(crate) enum LoginMethod { @@ -40,7 +38,7 @@ pub(crate) enum UserLoginMethod { pub(crate) enum ServiceAccountLoginMethod { AccessToken { access_token: AccessToken, - organization_id: Uuid, + organization_id: OrganizationId, state_file: Option, }, } diff --git a/crates/bitwarden-core/src/client/test_accounts.rs b/crates/bitwarden-core/src/client/test_accounts.rs index 48a5cdba9..e49f1f1bf 100644 --- a/crates/bitwarden-core/src/client/test_accounts.rs +++ b/crates/bitwarden-core/src/client/test_accounts.rs @@ -5,7 +5,7 @@ use bitwarden_crypto::{EncString, Kdf}; use crate::{ key_management::crypto::{InitOrgCryptoRequest, InitUserCryptoMethod, InitUserCryptoRequest}, - Client, + Client, UserId, }; impl Client { @@ -118,7 +118,7 @@ pub struct TestAccount { pub fn test_bitwarden_com_account() -> TestAccount { TestAccount { user: InitUserCryptoRequest { - user_id: Some(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8")), + user_id: Some(UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"))), kdf_params: Kdf::PBKDF2 { iterations: 600_000.try_into().unwrap(), }, @@ -178,7 +178,7 @@ pub fn test_bitwarden_com_account() -> TestAccount { pub fn test_legacy_user_key_account() -> TestAccount { TestAccount { user: InitUserCryptoRequest { - user_id: Some(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8")), + user_id: Some(UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"))), kdf_params: Kdf::PBKDF2 { iterations: 600_000.try_into().unwrap(), }, diff --git a/crates/bitwarden-core/src/ids.rs b/crates/bitwarden-core/src/ids.rs index e86527044..ec010cdbc 100644 --- a/crates/bitwarden-core/src/ids.rs +++ b/crates/bitwarden-core/src/ids.rs @@ -1,3 +1,4 @@ -use bitwarden_uuid::uuid; +use bitwarden_uuid::uuid_newtype; -uuid!(pub OrganizationId); +uuid_newtype!(pub OrganizationId); +uuid_newtype!(pub UserId); diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index 1aaca34c2..bf14e869d 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -21,7 +21,7 @@ use {tsify_next::Tsify, wasm_bindgen::prelude::*}; use crate::{ client::{encryption_settings::EncryptionSettingsError, LoginMethod, UserLoginMethod}, key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId}, - Client, NotAuthenticatedError, VaultLockedError, WrongPasswordError, + Client, NotAuthenticatedError, OrganizationId, UserId, VaultLockedError, WrongPasswordError, }; /// Catch all error for mobile crypto operations. @@ -44,7 +44,7 @@ pub enum CryptoClientError { #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub struct InitUserCryptoRequest { /// The user's ID. - pub user_id: Option, + pub user_id: Option, /// The user's KDF parameters, as received from the prelogin request pub kdf_params: Kdf, /// The user's email address @@ -247,7 +247,7 @@ pub(super) async fn initialize_user_crypto( #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub struct InitOrgCryptoRequest { /// The encryption keys for all the organizations the user is a part of - pub organization_keys: HashMap, + pub organization_keys: HashMap, } /// Initialize the user's organizational cryptographic state. @@ -633,7 +633,7 @@ mod tests { initialize_user_crypto( & client, InitUserCryptoRequest { - user_id: Some(uuid::Uuid::new_v4()), + user_id: Some(UserId::new_v4()), kdf_params: kdf.clone(), email: "test@bitwarden.com".into(), private_key: priv_key.to_owned(), @@ -654,7 +654,7 @@ mod tests { initialize_user_crypto( &client2, InitUserCryptoRequest { - user_id: Some(uuid::Uuid::new_v4()), + user_id: Some(UserId::new_v4()), kdf_params: kdf.clone(), email: "test@bitwarden.com".into(), private_key: priv_key.to_owned(), @@ -711,7 +711,7 @@ mod tests { initialize_user_crypto( & client, InitUserCryptoRequest { - user_id: Some(uuid::Uuid::new_v4()), + user_id: Some(UserId::new_v4()), kdf_params: Kdf::PBKDF2 { iterations: 100_000.try_into().unwrap(), }, @@ -734,7 +734,7 @@ mod tests { initialize_user_crypto( &client2, InitUserCryptoRequest { - user_id: Some(uuid::Uuid::new_v4()), + user_id: Some(UserId::new_v4()), kdf_params: Kdf::PBKDF2 { iterations: 100_000.try_into().unwrap(), }, @@ -778,7 +778,7 @@ mod tests { initialize_user_crypto( &client3, InitUserCryptoRequest { - user_id: Some(uuid::Uuid::new_v4()), + user_id: Some(UserId::new_v4()), kdf_params: Kdf::PBKDF2 { iterations: 100_000.try_into().unwrap(), }, diff --git a/crates/bitwarden-core/src/key_management/mod.rs b/crates/bitwarden-core/src/key_management/mod.rs index 972e1b1dc..1022f2474 100644 --- a/crates/bitwarden-core/src/key_management/mod.rs +++ b/crates/bitwarden-core/src/key_management/mod.rs @@ -9,6 +9,8 @@ //! [Encryptable](bitwarden_crypto::Encryptable) and [Decryptable](bitwarden_crypto::Encryptable). use bitwarden_crypto::{key_ids, KeyStore, SymmetricCryptoKey}; +use crate::OrganizationId; + pub mod crypto; mod crypto_client; @@ -19,7 +21,7 @@ key_ids! { pub enum SymmetricKeyId { Master, User, - Organization(uuid::Uuid), + Organization(OrganizationId), #[local] Local(&'static str), } @@ -60,7 +62,7 @@ pub fn create_test_crypto_with_user_key(key: SymmetricCryptoKey) -> KeyStore KeyStore { let store = KeyStore::default(); diff --git a/crates/bitwarden-core/tests/register.rs b/crates/bitwarden-core/tests/register.rs index 2b93f8227..c8393e83d 100644 --- a/crates/bitwarden-core/tests/register.rs +++ b/crates/bitwarden-core/tests/register.rs @@ -8,7 +8,7 @@ async fn test_register_initialize_crypto() { use bitwarden_core::{ key_management::crypto::{InitUserCryptoMethod, InitUserCryptoRequest}, - Client, + Client, UserId, }; use bitwarden_crypto::Kdf; @@ -29,7 +29,7 @@ async fn test_register_initialize_crypto() { client .crypto() .initialize_user_crypto(InitUserCryptoRequest { - user_id: Some(uuid::Uuid::new_v4()), + user_id: Some(UserId::new_v4()), kdf_params: kdf, email: email.to_owned(), private_key: register_response.keys.private, diff --git a/crates/bitwarden-exporters/src/lib.rs b/crates/bitwarden-exporters/src/lib.rs index 89cb243cc..8809ba46f 100644 --- a/crates/bitwarden-exporters/src/lib.rs +++ b/crates/bitwarden-exporters/src/lib.rs @@ -3,7 +3,7 @@ use std::fmt; use bitwarden_vault::{ - CipherRepromptType, CipherView, Fido2CredentialFullView, LoginUriView, UriMatchType, + CipherRepromptType, CipherView, Fido2CredentialFullView, FolderId, LoginUriView, UriMatchType, }; use chrono::{DateTime, Utc}; use uuid::Uuid; @@ -123,7 +123,7 @@ impl From for CipherView { Self { id: None, organization_id: None, - folder_id: value.folder_id, + folder_id: value.folder_id.map(FolderId::new), collection_ids: vec![], key: None, name: value.name, diff --git a/crates/bitwarden-exporters/src/models.rs b/crates/bitwarden-exporters/src/models.rs index 97e19c059..3bc3a85c4 100644 --- a/crates/bitwarden-exporters/src/models.rs +++ b/crates/bitwarden-exporters/src/models.rs @@ -10,7 +10,7 @@ impl TryFrom for crate::Folder { fn try_from(value: FolderView) -> Result { Ok(Self { - id: require!(value.id), + id: require!(value.id).into(), name: value.name, }) } @@ -44,8 +44,8 @@ impl crate::Cipher { }; Ok(Self { - id: require!(view.id), - folder_id: view.folder_id, + id: require!(view.id).into(), + folder_id: view.folder_id.map(|f| f.into()), name: view.name, notes: view.notes, r#type: r, @@ -200,7 +200,7 @@ impl From for crate::SecureNoteType { mod tests { use bitwarden_core::key_management::create_test_crypto_with_user_key; use bitwarden_crypto::SymmetricCryptoKey; - use bitwarden_vault::{CipherRepromptType, LoginView}; + use bitwarden_vault::{CipherId, CipherRepromptType, FolderId, LoginView}; use chrono::{DateTime, Utc}; use super::*; @@ -209,7 +209,7 @@ mod tests { fn test_try_from_folder_view() { let test_id: uuid::Uuid = "fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap(); let view = FolderView { - id: Some(test_id), + id: Some(FolderId::new(test_id)), name: "test_name".to_string(), revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), }; @@ -237,7 +237,7 @@ mod tests { autofill_on_page_load: None, fido2_credentials: None, }), - id: Some(test_id), + id: Some(CipherId::new(test_id)), organization_id: None, folder_id: None, collection_ids: vec![], @@ -288,7 +288,7 @@ mod tests { autofill_on_page_load: None, fido2_credentials: None, }), - id: Some(test_id), + id: Some(CipherId::new(test_id)), organization_id: None, folder_id: None, collection_ids: vec![], diff --git a/crates/bitwarden-fido/src/types.rs b/crates/bitwarden-fido/src/types.rs index b633c71fd..410124596 100644 --- a/crates/bitwarden-fido/src/types.rs +++ b/crates/bitwarden-fido/src/types.rs @@ -83,7 +83,8 @@ impl Fido2CredentialAutofillView { credential_id: string_to_guid_bytes(&c.credential_id)?, cipher_id: cipher .id - .ok_or(Fido2CredentialAutofillViewError::MissingCipherId)?, + .ok_or(Fido2CredentialAutofillViewError::MissingCipherId)? + .into(), rp_id: c.rp_id.clone(), user_handle: user_handle?, user_name_for_ui: c @@ -121,7 +122,8 @@ impl Fido2CredentialAutofillView { credential_id: string_to_guid_bytes(&c.credential_id)?, cipher_id: cipher .id - .ok_or(Fido2CredentialAutofillViewError::MissingCipherId)?, + .ok_or(Fido2CredentialAutofillViewError::MissingCipherId)? + .into(), rp_id: c.rp_id.clone(), user_handle: user_handle?, user_name_for_ui: c diff --git a/crates/bitwarden-uniffi/src/vault/ciphers.rs b/crates/bitwarden-uniffi/src/vault/ciphers.rs index 8436b9307..92c4f56af 100644 --- a/crates/bitwarden-uniffi/src/vault/ciphers.rs +++ b/crates/bitwarden-uniffi/src/vault/ciphers.rs @@ -1,6 +1,5 @@ use bitwarden_core::OrganizationId; use bitwarden_vault::{Cipher, CipherListView, CipherView, EncryptionContext, Fido2CredentialView}; -use uuid::Uuid; use crate::{error::Error, Result}; @@ -39,11 +38,11 @@ impl CiphersClient { pub fn move_to_organization( &self, cipher: CipherView, - organization_id: Uuid, + organization_id: OrganizationId, ) -> Result { Ok(self .0 - .move_to_organization(cipher, OrganizationId::new(organization_id)) + .move_to_organization(cipher, organization_id) .map_err(Error::Cipher)?) } } diff --git a/crates/bitwarden-uuid-macro/src/lib.rs b/crates/bitwarden-uuid-macro/src/lib.rs index 401df700d..afde55385 100644 --- a/crates/bitwarden-uuid-macro/src/lib.rs +++ b/crates/bitwarden-uuid-macro/src/lib.rs @@ -9,7 +9,7 @@ use syn::{ #[allow(missing_docs)] #[proc_macro] -pub fn uuid(input: TokenStream) -> TokenStream { +pub fn uuid_newtype(input: TokenStream) -> TokenStream { // Parse input as: vis ident let input = parse_macro_input!(input as IdTypeInput); let ident = input.ident; @@ -24,8 +24,8 @@ pub fn uuid(input: TokenStream) -> TokenStream { #[cfg_attr(feature = "wasm", derive(::tsify_next::Tsify), tsify(into_wasm_abi, from_wasm_abi))] #[derive( ::serde::Serialize, ::serde::Deserialize, - ::std::cmp::PartialEq, ::std::cmp::Eq, - ::std::clone::Clone, ::std::marker::Copy, ::std::fmt::Debug + ::std::cmp::PartialEq, ::std::cmp::Eq, ::std::cmp::PartialOrd, ::std::cmp::Ord, + ::std::hash::Hash, ::std::clone::Clone, ::std::marker::Copy, ::std::fmt::Debug )] #[repr(transparent)] #vis struct #ident @@ -42,6 +42,11 @@ pub fn uuid(input: TokenStream) -> TokenStream { pub fn new(value: uuid::Uuid) -> Self { Self(value) } + + /// Create a new UUID v4 based id. + pub fn new_v4() -> Self { + Self(uuid::Uuid::new_v4()) + } } impl ::std::str::FromStr for #ident { diff --git a/crates/bitwarden-uuid/src/lib.rs b/crates/bitwarden-uuid/src/lib.rs index 870be9095..3cc268215 100644 --- a/crates/bitwarden-uuid/src/lib.rs +++ b/crates/bitwarden-uuid/src/lib.rs @@ -1,3 +1,3 @@ #![doc = include_str!("../README.md")] -pub use bitwarden_uuid_macro::uuid; +pub use bitwarden_uuid_macro::uuid_newtype; diff --git a/crates/bitwarden-uuid/tests/uuid.rs b/crates/bitwarden-uuid/tests/uuid.rs index 4f4b7ece6..b67ec6394 100644 --- a/crates/bitwarden-uuid/tests/uuid.rs +++ b/crates/bitwarden-uuid/tests/uuid.rs @@ -1,8 +1,8 @@ #![allow(unexpected_cfgs, missing_docs)] -use bitwarden_uuid::uuid; +use bitwarden_uuid::uuid_newtype; -uuid!(TestId); +uuid_newtype!(TestId); #[test] fn test_parse_string() { diff --git a/crates/bitwarden-vault/Cargo.toml b/crates/bitwarden-vault/Cargo.toml index 01d496d55..13b788b63 100644 --- a/crates/bitwarden-vault/Cargo.toml +++ b/crates/bitwarden-vault/Cargo.toml @@ -32,6 +32,7 @@ bitwarden-api-api = { workspace = true } bitwarden-core = { workspace = true, features = ["internal"] } bitwarden-crypto = { workspace = true } bitwarden-error = { workspace = true } +bitwarden-uuid = { workspace = true } chrono = { workspace = true } data-encoding = ">=2.0, <3" hmac = ">=0.12.1, <0.13" diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 0d4c515dd..a1c7c0f65 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -1,19 +1,19 @@ use bitwarden_api_api::models::CipherDetailsResponseModel; use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, - require, MissingFieldError, VaultLockedError, + require, MissingFieldError, OrganizationId, UserId, VaultLockedError, }; use bitwarden_crypto::{ CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, }; use bitwarden_error::bitwarden_error; +use bitwarden_uuid::uuid_newtype; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; use thiserror::Error; #[cfg(feature = "wasm")] use tsify_next::Tsify; -use uuid::Uuid; #[cfg(feature = "wasm")] use wasm_bindgen::prelude::wasm_bindgen; @@ -27,10 +27,12 @@ use super::{ secure_note, ssh_key, }; use crate::{ - password_history, Fido2CredentialFullView, Fido2CredentialView, Login, LoginView, - VaultParseError, + password_history, CollectionId, Fido2CredentialFullView, Fido2CredentialView, FolderId, Login, + LoginView, VaultParseError, }; +uuid_newtype!(pub CipherId); + #[allow(missing_docs)] #[bitwarden_error(flat)] #[derive(Debug, Error)] @@ -89,7 +91,7 @@ pub enum CipherRepromptType { pub struct EncryptionContext { /// The Id of the user that encrypted the cipher. It should always represent a UserId, even for /// Organization-owned ciphers - pub encrypted_for: Uuid, + pub encrypted_for: UserId, pub cipher: Cipher, } @@ -99,10 +101,10 @@ pub struct EncryptionContext { #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub struct Cipher { - pub id: Option, - pub organization_id: Option, - pub folder_id: Option, - pub collection_ids: Vec, + pub id: Option, + pub organization_id: Option, + pub folder_id: Option, + pub collection_ids: Vec, /// More recent ciphers uses individual encryption keys to encrypt the other fields of the /// Cipher. @@ -141,10 +143,10 @@ pub struct Cipher { #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub struct CipherView { - pub id: Option, - pub organization_id: Option, - pub folder_id: Option, - pub collection_ids: Vec, + pub id: Option, + pub organization_id: Option, + pub folder_id: Option, + pub collection_ids: Vec, /// Temporary, required to support re-encrypting existing items. pub key: Option, @@ -213,10 +215,10 @@ pub enum CopyableCipherFields { #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub struct CipherListView { - pub id: Option, - pub organization_id: Option, - pub folder_id: Option, - pub collection_ids: Vec, + pub id: Option, + pub organization_id: Option, + pub folder_id: Option, + pub collection_ids: Vec, /// Temporary, required to support calculating TOTP from CipherListView. pub key: Option, @@ -508,7 +510,7 @@ impl CipherView { pub fn move_to_organization( &mut self, ctx: &mut KeyStoreContext, - organization_id: Uuid, + organization_id: OrganizationId, ) -> Result<(), CipherError> { let old_key = self.key_identifier(); let new_key = SymmetricKeyId::Organization(organization_id); @@ -668,10 +670,15 @@ impl TryFrom for Cipher { fn try_from(cipher: CipherDetailsResponseModel) -> Result { Ok(Self { - id: cipher.id, - organization_id: cipher.organization_id, - folder_id: cipher.folder_id, - collection_ids: cipher.collection_ids.unwrap_or_default(), + id: cipher.id.map(CipherId::new), + organization_id: cipher.organization_id.map(OrganizationId::new), + folder_id: cipher.folder_id.map(FolderId::new), + collection_ids: cipher + .collection_ids + .unwrap_or_default() + .into_iter() + .map(CollectionId::new) + .collect(), name: require!(EncString::try_from_optional(cipher.name)?), notes: EncString::try_from_optional(cipher.notes)?, r#type: require!(cipher.r#type).into(), @@ -746,7 +753,7 @@ mod tests { use crate::{login::Fido2CredentialListView, Fido2Credential}; fn generate_cipher() -> CipherView { - let test_id: uuid::Uuid = "fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap(); + let test_id = "fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap(); CipherView { r#type: CipherType::Login, login: Some(LoginView { @@ -973,7 +980,7 @@ mod tests { #[test] fn test_move_user_cipher_to_org() { - let org = uuid::Uuid::new_v4(); + let org = OrganizationId::new_v4(); let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let org_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let key_store = create_test_crypto_with_user_and_org_key(key, org, org_key); @@ -998,7 +1005,7 @@ mod tests { #[test] fn test_move_user_cipher_to_org_manually() { - let org = uuid::Uuid::new_v4(); + let org = OrganizationId::new_v4(); let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let org_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let key_store = create_test_crypto_with_user_and_org_key(key, org, org_key); @@ -1018,7 +1025,7 @@ mod tests { #[test] fn test_move_user_cipher_with_attachment_without_key_to_org() { - let org = uuid::Uuid::new_v4(); + let org = OrganizationId::new_v4(); let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let org_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let key_store = create_test_crypto_with_user_and_org_key(key, org, org_key); @@ -1042,7 +1049,7 @@ mod tests { #[test] fn test_move_user_cipher_with_attachment_with_key_to_org() { - let org = uuid::Uuid::new_v4(); + let org = OrganizationId::new_v4(); let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let org_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let key_store = create_test_crypto_with_user_and_org_key(key, org, org_key); @@ -1110,7 +1117,7 @@ mod tests { #[test] fn test_move_user_cipher_with_key_with_attachment_with_key_to_org() { - let org = uuid::Uuid::new_v4(); + let org = OrganizationId::new_v4(); let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let org_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); let key_store = create_test_crypto_with_user_and_org_key(key, org, org_key); diff --git a/crates/bitwarden-vault/src/cipher/cipher_client.rs b/crates/bitwarden-vault/src/cipher/cipher_client.rs index a2bd337c7..b76d820bd 100644 --- a/crates/bitwarden-vault/src/cipher/cipher_client.rs +++ b/crates/bitwarden-vault/src/cipher/cipher_client.rs @@ -74,7 +74,7 @@ impl CiphersClient { organization_id: OrganizationId, ) -> Result { let key_store = self.client.internal.get_key_store(); - cipher_view.move_to_organization(&mut key_store.context(), organization_id.into())?; + cipher_view.move_to_organization(&mut key_store.context(), organization_id)?; Ok(cipher_view) } diff --git a/crates/bitwarden-vault/src/cipher/mod.rs b/crates/bitwarden-vault/src/cipher/mod.rs index af162487c..9b4d38a70 100644 --- a/crates/bitwarden-vault/src/cipher/mod.rs +++ b/crates/bitwarden-vault/src/cipher/mod.rs @@ -19,8 +19,8 @@ pub use attachment::{ pub use attachment_client::{AttachmentsClient, DecryptFileError, EncryptFileError}; pub use card::{CardBrand, CardListView, CardView}; pub use cipher::{ - Cipher, CipherError, CipherListView, CipherListViewType, CipherRepromptType, CipherType, - CipherView, EncryptionContext, + Cipher, CipherError, CipherId, CipherListView, CipherListViewType, CipherRepromptType, + CipherType, CipherView, EncryptionContext, }; pub use cipher_client::CiphersClient; pub use field::FieldView; diff --git a/crates/bitwarden-vault/src/collection.rs b/crates/bitwarden-vault/src/collection.rs index 91224c92d..9764707bf 100644 --- a/crates/bitwarden-vault/src/collection.rs +++ b/crates/bitwarden-vault/src/collection.rs @@ -1,14 +1,16 @@ use bitwarden_api_api::models::CollectionDetailsResponseModel; use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, - require, + require, OrganizationId, }; use bitwarden_crypto::{CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext}; +use bitwarden_uuid::uuid_newtype; use serde::{Deserialize, Serialize}; -use uuid::Uuid; use crate::VaultParseError; +uuid_newtype!(pub CollectionId); + #[allow(missing_docs)] #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -19,8 +21,8 @@ use crate::VaultParseError; tsify(into_wasm_abi, from_wasm_abi) )] pub struct Collection { - pub id: Option, - pub organization_id: Uuid, + pub id: Option, + pub organization_id: OrganizationId, pub name: EncString, @@ -35,8 +37,8 @@ pub struct Collection { #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] pub struct CollectionView { - pub id: Option, - pub organization_id: Uuid, + pub id: Option, + pub organization_id: OrganizationId, pub name: String, @@ -77,8 +79,8 @@ impl TryFrom for Collection { fn try_from(collection: CollectionDetailsResponseModel) -> Result { Ok(Collection { - id: collection.id, - organization_id: require!(collection.organization_id), + id: collection.id.map(CollectionId::new), + organization_id: OrganizationId::new(require!(collection.organization_id)), name: require!(collection.name).parse()?, external_id: collection.external_id, hide_passwords: collection.hide_passwords.unwrap_or(false), diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index 18083a5f5..ca57c2e88 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -6,21 +6,23 @@ use bitwarden_core::{ use bitwarden_crypto::{ CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, }; +use bitwarden_uuid::uuid_newtype; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use uuid::Uuid; #[cfg(feature = "wasm")] use {tsify_next::Tsify, wasm_bindgen::prelude::*}; use crate::VaultParseError; +uuid_newtype!(pub FolderId); + #[allow(missing_docs)] #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub struct Folder { - id: Option, + id: Option, name: EncString, revision_date: DateTime, } @@ -31,7 +33,7 @@ pub struct Folder { #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] pub struct FolderView { - pub id: Option, + pub id: Option, pub name: String, pub revision_date: DateTime, } @@ -80,7 +82,7 @@ impl TryFrom for Folder { fn try_from(folder: FolderResponseModel) -> Result { Ok(Folder { - id: folder.id, + id: folder.id.map(FolderId::new), name: require!(EncString::try_from_optional(folder.name)?), revision_date: require!(folder.revision_date).parse()?, }) diff --git a/crates/bitwarden-vault/src/lib.rs b/crates/bitwarden-vault/src/lib.rs index e1d893cc2..fb0478223 100644 --- a/crates/bitwarden-vault/src/lib.rs +++ b/crates/bitwarden-vault/src/lib.rs @@ -8,11 +8,11 @@ mod uniffi_support; mod cipher; pub use cipher::*; mod collection; -pub use collection::{Collection, CollectionView}; +pub use collection::{Collection, CollectionId, CollectionView}; mod collection_client; pub use collection_client::CollectionsClient; mod folder; -pub use folder::{Folder, FolderView}; +pub use folder::{Folder, FolderId, FolderView}; mod folder_client; pub use folder_client::FoldersClient; mod password_history; diff --git a/crates/bitwarden-vault/src/sync.rs b/crates/bitwarden-vault/src/sync.rs index df959a7d1..d0bb90df4 100644 --- a/crates/bitwarden-vault/src/sync.rs +++ b/crates/bitwarden-vault/src/sync.rs @@ -3,10 +3,10 @@ use bitwarden_api_api::models::{ }; use bitwarden_core::{ client::encryption_settings::EncryptionSettingsError, require, Client, MissingFieldError, + OrganizationId, UserId, }; use serde::{Deserialize, Serialize}; use thiserror::Error; -use uuid::Uuid; use crate::{Cipher, Collection, Folder, GlobalDomains, VaultParseError}; @@ -42,6 +42,7 @@ pub(crate) async fn sync(client: &Client, input: &SyncRequest) -> Result Result Result { Ok(ProfileOrganizationResponse { - id: require!(response.id), + id: OrganizationId::new(require!(response.id)), }) } } @@ -131,7 +132,7 @@ impl ProfileResponse { response: ProfileResponseModel, ) -> Result { Ok(ProfileResponse { - id: require!(response.id), + id: UserId::new(require!(response.id)), name: require!(response.name), email: require!(response.email), //key: response.key, diff --git a/crates/bitwarden-wasm-internal/src/custom_types.rs b/crates/bitwarden-wasm-internal/src/custom_types.rs index f910d1173..cae9ead52 100644 --- a/crates/bitwarden-wasm-internal/src/custom_types.rs +++ b/crates/bitwarden-wasm-internal/src/custom_types.rs @@ -10,10 +10,7 @@ import { Tagged } from "type-fest"; * * Never create or cast to this type directly, use the `uuid()` function instead. */ -// TODO: Uncomment this when the `uuid` crate is used. -// export type Uuid = unknown; - -export type Uuid = string; +export type Uuid = unknown; /** * RFC3339 compliant date-time string.