diff --git a/Cargo.lock b/Cargo.lock index 02e6043a3..e00722783 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1138,6 +1138,7 @@ dependencies = [ "bitwarden-state", "bitwarden-test", "bitwarden-uuid", + "bitwarden-vault", "chrono", "reqwest", "serde", diff --git a/crates/bitwarden-api-api/.openapi-generator/FILES b/crates/bitwarden-api-api/.openapi-generator/FILES index 492916b62..f20885047 100644 --- a/crates/bitwarden-api-api/.openapi-generator/FILES +++ b/crates/bitwarden-api-api/.openapi-generator/FILES @@ -528,6 +528,8 @@ src/models/selection_read_only_request_model.rs src/models/selection_read_only_response_model.rs src/models/self_hosted_organization_license_request_model.rs src/models/send_access_response_model.rs +src/models/send_data_model.rs +src/models/send_encryption_type.rs src/models/send_file_download_data_response_model.rs src/models/send_file_model.rs src/models/send_file_upload_data_response_model.rs diff --git a/crates/bitwarden-api-api/src/models/mod.rs b/crates/bitwarden-api-api/src/models/mod.rs index 7afcb87df..d87c2cc64 100644 --- a/crates/bitwarden-api-api/src/models/mod.rs +++ b/crates/bitwarden-api-api/src/models/mod.rs @@ -906,6 +906,10 @@ pub mod self_hosted_organization_license_request_model; pub use self::self_hosted_organization_license_request_model::SelfHostedOrganizationLicenseRequestModel; pub mod send_access_response_model; pub use self::send_access_response_model::SendAccessResponseModel; +pub mod send_data_model; +pub use self::send_data_model::SendDataModel; +pub mod send_encryption_type; +pub use self::send_encryption_type::SendEncryptionType; pub mod send_file_download_data_response_model; pub use self::send_file_download_data_response_model::SendFileDownloadDataResponseModel; pub mod send_file_model; diff --git a/crates/bitwarden-api-api/src/models/send_access_response_model.rs b/crates/bitwarden-api-api/src/models/send_access_response_model.rs index 382257cc3..aec634e62 100644 --- a/crates/bitwarden-api-api/src/models/send_access_response_model.rs +++ b/crates/bitwarden-api-api/src/models/send_access_response_model.rs @@ -56,13 +56,12 @@ pub struct SendAccessResponseModel { skip_serializing_if = "Option::is_none" )] pub text: Option>, - /// Encrypted string containing secret Send data #[serde( rename = "data", alias = "Data", skip_serializing_if = "Option::is_none" )] - pub data: Option, + pub data: Option>, /// The date after which a send cannot be accessed. When this value is null, there is no /// expiration date. #[serde( diff --git a/crates/bitwarden-api-api/src/models/send_data_model.rs b/crates/bitwarden-api-api/src/models/send_data_model.rs new file mode 100644 index 000000000..7cf30d55f --- /dev/null +++ b/crates/bitwarden-api-api/src/models/send_data_model.rs @@ -0,0 +1,38 @@ +/* + * Bitwarden Internal API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: latest + * + * Generated by: https://openapi-generator.tech + */ + +use serde::{Deserialize, Serialize}; + +use crate::models; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct SendDataModel { + #[serde( + rename = "encryptionVersion", + alias = "EncryptionVersion", + skip_serializing_if = "Option::is_none" + )] + pub encryption_version: Option, + #[serde( + rename = "data", + alias = "Data", + skip_serializing_if = "Option::is_none" + )] + pub data: Option, +} + +impl SendDataModel { + pub fn new() -> SendDataModel { + SendDataModel { + encryption_version: None, + data: None, + } + } +} diff --git a/crates/bitwarden-api-api/src/models/send_encryption_type.rs b/crates/bitwarden-api-api/src/models/send_encryption_type.rs new file mode 100644 index 000000000..4bd4ca0fc --- /dev/null +++ b/crates/bitwarden-api-api/src/models/send_encryption_type.rs @@ -0,0 +1,78 @@ +/* + * Bitwarden Internal API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: latest + * + * Generated by: https://openapi-generator.tech + */ + +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor}; + +use crate::models; +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum SendEncryptionType { + V1, + + /// Unknown value returned from the server. This is used to handle forward compatibility. + __Unknown(i64), +} + +impl SendEncryptionType { + pub fn as_i64(&self) -> i64 { + match self { + Self::V1 => 1, + Self::__Unknown(v) => *v, + } + } + + pub fn from_i64(value: i64) -> Self { + match value { + 1 => Self::V1, + v => Self::__Unknown(v), + } + } +} + +impl serde::Serialize for SendEncryptionType { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_i64(self.as_i64()) + } +} + +impl<'de> serde::Deserialize<'de> for SendEncryptionType { + fn deserialize>(deserializer: D) -> Result { + struct SendEncryptionTypeVisitor; + + impl Visitor<'_> for SendEncryptionTypeVisitor { + type Value = SendEncryptionType; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("an integer") + } + + fn visit_i64(self, v: i64) -> Result { + Ok(SendEncryptionType::from_i64(v)) + } + + fn visit_u64(self, v: u64) -> Result { + Ok(SendEncryptionType::from_i64(v as i64)) + } + } + + deserializer.deserialize_i64(SendEncryptionTypeVisitor) + } +} + +impl std::fmt::Display for SendEncryptionType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.as_i64()) + } +} +impl Default for SendEncryptionType { + fn default() -> SendEncryptionType { + Self::V1 + } +} diff --git a/crates/bitwarden-api-api/src/models/send_request_model.rs b/crates/bitwarden-api-api/src/models/send_request_model.rs index d400825d6..379ef184b 100644 --- a/crates/bitwarden-api-api/src/models/send_request_model.rs +++ b/crates/bitwarden-api-api/src/models/send_request_model.rs @@ -86,13 +86,12 @@ pub struct SendRequestModel { skip_serializing_if = "Option::is_none" )] pub text: Option>, - /// String containing secret Send data #[serde( rename = "data", alias = "Data", skip_serializing_if = "Option::is_none" )] - pub data: Option, + pub data: Option>, /// Base64-encoded byte array of a password hash that grants access to the send. Mutually /// exclusive with Bit.Api.Tools.Models.Request.SendRequestModel.Emails. #[serde( diff --git a/crates/bitwarden-api-api/src/models/send_response_model.rs b/crates/bitwarden-api-api/src/models/send_response_model.rs index 0e9891652..c5159db55 100644 --- a/crates/bitwarden-api-api/src/models/send_response_model.rs +++ b/crates/bitwarden-api-api/src/models/send_response_model.rs @@ -69,13 +69,12 @@ pub struct SendResponseModel { skip_serializing_if = "Option::is_none" )] pub text: Option>, - /// Encrypted string containing secret Send data #[serde( rename = "data", alias = "Data", skip_serializing_if = "Option::is_none" )] - pub data: Option, + pub data: Option>, /// A base64-encoded byte array containing the Send's encryption key. It's also provided to /// send recipients in the Send's URL. #[serde(rename = "key", alias = "Key", skip_serializing_if = "Option::is_none")] diff --git a/crates/bitwarden-api-api/src/models/send_with_id_request_model.rs b/crates/bitwarden-api-api/src/models/send_with_id_request_model.rs index feb0d43de..1f3975197 100644 --- a/crates/bitwarden-api-api/src/models/send_with_id_request_model.rs +++ b/crates/bitwarden-api-api/src/models/send_with_id_request_model.rs @@ -86,13 +86,12 @@ pub struct SendWithIdRequestModel { skip_serializing_if = "Option::is_none" )] pub text: Option>, - /// String containing secret Send data #[serde( rename = "data", alias = "Data", skip_serializing_if = "Option::is_none" )] - pub data: Option, + pub data: Option>, /// Base64-encoded byte array of a password hash that grants access to the send. Mutually /// exclusive with Bit.Api.Tools.Models.Request.SendRequestModel.Emails. #[serde( diff --git a/crates/bitwarden-api-identity/README.md b/crates/bitwarden-api-identity/README.md index aacfa49e8..59bf59a50 100644 --- a/crates/bitwarden-api-identity/README.md +++ b/crates/bitwarden-api-identity/README.md @@ -22,7 +22,7 @@ client. - API version: v1 - Package version: 3.0.0 - Server Git commit: - [`c2d97d5ff2019c524405c36f7f3afc992ec0ef03`](https://github.com/bitwarden/server/commit/c2d97d5ff2019c524405c36f7f3afc992ec0ef03) + [`4f8c0f010712cbd860c29b225bec84b2592a331c`](https://github.com/bitwarden/server/commit/4f8c0f010712cbd860c29b225bec84b2592a331c) - Generator version: 7.15.0 - Build package: `org.openapitools.codegen.languages.RustClientCodegen` @@ -39,7 +39,6 @@ All URIs are relative to *https://identity.bitwarden.com* | _AccountsApi_ | [**post_register_verification_email_clicked**](docs/AccountsApi.md#accounts_post_register_verification_email_clicked) | **POST** /accounts/register/verification-email-clicked | | _AccountsApi_ | [**post_trial_initiation_send_verification_email**](docs/AccountsApi.md#accounts_post_trial_initiation_send_verification_email) | **POST** /accounts/trial/send-verification-email | | _InfoApi_ | [**get_alive**](docs/InfoApi.md#info_get_alive) | **GET** /alive | -| _InfoApi_ | [**get_version**](docs/InfoApi.md#info_get_version) | **GET** /version | | _SsoApi_ | [**external_callback**](docs/SsoApi.md#sso_external_callback) | **GET** /sso/ExternalCallback | | _SsoApi_ | [**external_challenge**](docs/SsoApi.md#sso_external_challenge) | **GET** /sso/ExternalChallenge | | _SsoApi_ | [**login**](docs/SsoApi.md#sso_login) | **GET** /sso/Login | diff --git a/crates/bitwarden-send/Cargo.toml b/crates/bitwarden-send/Cargo.toml index 74afcb4a5..d1664eb41 100644 --- a/crates/bitwarden-send/Cargo.toml +++ b/crates/bitwarden-send/Cargo.toml @@ -34,9 +34,11 @@ bitwarden-encoding = { workspace = true } bitwarden-error = { workspace = true } bitwarden-state = { workspace = true } bitwarden-uuid = { workspace = true } +bitwarden-vault = { workspace = true } chrono = { workspace = true } reqwest = { workspace = true } serde = { workspace = true } +serde_json = { workspace = true } serde_repr = { workspace = true } sha2 = { workspace = true } thiserror = { workspace = true } @@ -50,7 +52,6 @@ zeroize = { workspace = true } [dev-dependencies] bitwarden-api-api = { workspace = true, features = ["mockall"] } bitwarden-test = { workspace = true } -serde_json = { workspace = true } tokio = { workspace = true, features = ["rt"] } wiremock = { workspace = true } diff --git a/crates/bitwarden-send/src/access.rs b/crates/bitwarden-send/src/access.rs index 5ef8f8a42..764737f48 100644 --- a/crates/bitwarden-send/src/access.rs +++ b/crates/bitwarden-send/src/access.rs @@ -614,6 +614,7 @@ mod tests { text: Some(text.to_owned()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, diff --git a/crates/bitwarden-send/src/create.rs b/crates/bitwarden-send/src/create.rs index 636fa7569..0dbce127b 100644 --- a/crates/bitwarden-send/src/create.rs +++ b/crates/bitwarden-send/src/create.rs @@ -87,7 +87,8 @@ impl // Derive the shareable send key for encrypting content let send_key = Send::derive_shareable_key(ctx, &k)?; - let (send_type, file, text) = self.view_type.clone().encrypt_composite(ctx, send_key)?; + let (send_type, file, text, data) = + self.view_type.clone().encrypt_composite(ctx, send_key)?; let (password, emails) = self.auth.auth_data(&k); @@ -109,8 +110,7 @@ impl deletion_date: self.deletion_date.to_rfc3339(), file, text, - // TODO: Implement logic for item-based Sends - data: None, + data, password, emails, disabled: self.disabled, diff --git a/crates/bitwarden-send/src/delete.rs b/crates/bitwarden-send/src/delete.rs index 8b5b08a3c..6032aebbe 100644 --- a/crates/bitwarden-send/src/delete.rs +++ b/crates/bitwarden-send/src/delete.rs @@ -77,6 +77,7 @@ mod tests { text: Some("Secret text".to_string()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, diff --git a/crates/bitwarden-send/src/edit.rs b/crates/bitwarden-send/src/edit.rs index 220241637..f9899b97c 100644 --- a/crates/bitwarden-send/src/edit.rs +++ b/crates/bitwarden-send/src/edit.rs @@ -134,7 +134,7 @@ impl let send_key = Send::derive_shareable_key(ctx, &k)?; - let (send_type, file, text) = self + let (send_type, file, text, data) = self .request .view_type .clone() @@ -167,8 +167,7 @@ impl deletion_date: self.request.deletion_date.to_rfc3339(), file, text, - // TODO: Implement logic for item-based Sends - data: None, + data, password, emails, disabled: self.request.disabled, @@ -344,6 +343,7 @@ mod tests { text: Some("original text".to_string()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -511,6 +511,7 @@ mod tests { text: Some("original text".to_string()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -596,6 +597,7 @@ mod tests { text: Some("secret".to_string()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, diff --git a/crates/bitwarden-send/src/error.rs b/crates/bitwarden-send/src/error.rs index 6d69c1372..b5c4d058a 100644 --- a/crates/bitwarden-send/src/error.rs +++ b/crates/bitwarden-send/src/error.rs @@ -9,9 +9,16 @@ pub enum SendParseError { Crypto(#[from] bitwarden_crypto::CryptoError), #[error(transparent)] MissingField(#[from] bitwarden_core::MissingFieldError), + #[error(transparent)] + DeserializationFailure(#[from] SendItemDeserializationFailure), } /// Item does not exist error. #[derive(Debug, thiserror::Error)] #[error("Item does not exist")] pub struct ItemNotFoundError; + +/// Unable to deserialize Item-type Send data +#[derive(Debug, thiserror::Error)] +#[error("Send item deserialization failure")] +pub struct SendItemDeserializationFailure; diff --git a/crates/bitwarden-send/src/get_list.rs b/crates/bitwarden-send/src/get_list.rs index 20a666244..67cc5978f 100644 --- a/crates/bitwarden-send/src/get_list.rs +++ b/crates/bitwarden-send/src/get_list.rs @@ -98,6 +98,7 @@ mod tests { text: Some("Secret text".to_string()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -177,6 +178,7 @@ mod tests { text: Some("Text 1".to_string()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -209,6 +211,7 @@ mod tests { text: Some("Text 2".to_string()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, diff --git a/crates/bitwarden-send/src/remove_password.rs b/crates/bitwarden-send/src/remove_password.rs index b5f0346b5..03a4c980d 100644 --- a/crates/bitwarden-send/src/remove_password.rs +++ b/crates/bitwarden-send/src/remove_password.rs @@ -94,6 +94,7 @@ mod tests { text: Some("Secret text".to_string()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index 700fd388c..bc372afeb 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -1,5 +1,5 @@ use bitwarden_api_api::models::{ - SendFileModel, SendResponseModel, SendTextModel, SendWithIdRequestModel, + SendDataModel, SendFileModel, SendResponseModel, SendTextModel, SendWithIdRequestModel, }; use bitwarden_core::{ key_management::{KeySlotIds, SymmetricKeySlotId}, @@ -11,6 +11,7 @@ use bitwarden_crypto::{ }; use bitwarden_encoding::{B64, B64Url}; use bitwarden_uuid::uuid_newtype; +use bitwarden_vault::{Cipher, CipherView, EncryptMode}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -19,7 +20,7 @@ use zeroize::Zeroizing; #[cfg(feature = "wasm")] use {tsify::Tsify, wasm_bindgen::prelude::*}; -use crate::SendParseError; +use crate::{SendParseError, error::SendItemDeserializationFailure}; pub const SEND_ITERATIONS: u32 = 100_000; uuid_newtype!(pub SendId); @@ -71,6 +72,26 @@ pub struct SendText { pub hidden: bool, } +/// View model for decrypted SendItem +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Record))] +#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] +pub struct SendItemView { + /// The item content of the send + pub data: CipherView, +} + +/// Item-based send content +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Record))] +#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] +pub struct SendItem { + pub encryption_version: SendEncryptionType, + pub data: Cipher, +} + /// View model for decrypted SendText #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -83,7 +104,7 @@ pub struct SendTextView { pub hidden: bool, } -/// The type of Send, either text or file +/// The type of Send, either text, file, or item #[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug, PartialEq)] #[repr(u8)] #[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] @@ -113,6 +134,16 @@ pub enum AuthType { None = 2, } +/// Indicates the version of Send data encryption that is being used +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)] +#[repr(u8)] +#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] +#[cfg_attr(feature = "wasm", wasm_bindgen)] +pub enum SendEncryptionType { + /// V1 encryption (field by field) + V1 = 1, +} + /// Type-safe authentication method for a Send, including the authentication data. /// This ensures that password and email authentication are mutually exclusive. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -212,6 +243,8 @@ pub enum SendViewType { File(SendFileView), /// Text-based send Text(SendTextView), + /// Item-based send + Item(SendItemView), } /// Type alias for the tuple returned by SendViewType::into_api_models @@ -219,6 +252,7 @@ type SendApiModels = ( bitwarden_api_api::models::SendType, Option>, Option>, + Option>, ); impl CompositeEncryptable for SendViewType { @@ -237,6 +271,7 @@ impl CompositeEncryptable for Sen size_name: f.size_name.clone(), })), None, + None, )), SendViewType::Text(t) => Ok(( bitwarden_api_api::models::SendType::Text, @@ -250,7 +285,22 @@ impl CompositeEncryptable for Sen .map(|e| e.to_string()), hidden: Some(t.hidden), })), + None, )), + SendViewType::Item(i) => { + let encrypted = i.encrypt_composite(ctx, key)?; + let serialized_cipher = + serde_json::to_string(&encrypted.data).unwrap_or("{}".to_string()); + Ok(( + bitwarden_api_api::models::SendType::Item, + None, + None, + Some(Box::new(bitwarden_api_api::models::SendDataModel { + encryption_version: Some(SendEncryptionType::V1.into()), + data: Some(serialized_cipher), + })), + )) + } } } } @@ -272,6 +322,7 @@ pub struct Send { pub r#type: SendType, pub file: Option, pub text: Option, + pub data: Option, pub max_access_count: Option, pub access_count: u32, @@ -350,6 +401,7 @@ pub struct SendView { pub r#type: SendType, pub file: Option, pub text: Option, + pub data: Option, pub max_access_count: Option, pub access_count: u32, @@ -477,6 +529,31 @@ impl CompositeEncryptable for SendFile } } +impl Decryptable for SendItem { + fn decrypt( + &self, + ctx: &mut KeyStoreContext, + key: SymmetricKeySlotId, + ) -> Result { + let data: CipherView = self.data.decrypt(ctx, key)?; + Ok(SendItemView { data }) + } +} + +impl CompositeEncryptable for SendItemView { + fn encrypt_composite( + &self, + ctx: &mut KeyStoreContext, + key: SymmetricKeySlotId, + ) -> Result { + let cipher: Cipher = EncryptMode::Legacy(self.data.clone()).encrypt_composite(ctx, key)?; + Ok(SendItem { + encryption_version: SendEncryptionType::V1, + data: cipher, + }) + } +} + impl Decryptable for Send { fn decrypt( &self, @@ -502,6 +579,7 @@ impl Decryptable for Send { r#type: self.r#type, file: self.file.decrypt(ctx, key).ok().flatten(), text: self.text.decrypt(ctx, key).ok().flatten(), + data: self.data.decrypt(ctx, key).ok().flatten(), max_access_count: self.max_access_count, access_count: self.access_count, @@ -595,6 +673,7 @@ impl CompositeEncryptable for SendView { r#type: self.r#type, file: self.file.encrypt_composite(ctx, send_key)?, text: self.text.encrypt_composite(ctx, send_key)?, + data: self.data.encrypt_composite(ctx, send_key)?, max_access_count: self.max_access_count, access_count: self.access_count, @@ -637,6 +716,7 @@ impl TryFrom for Send { r#type: require!(send.r#type).try_into()?, file: send.file.map(|f| (*f).try_into()).transpose()?, text: send.text.map(|t| (*t).try_into()).transpose()?, + data: send.data.map(|d| (*d).try_into()).transpose()?, max_access_count: send.max_access_count.map(|s| s as u32), access_count: require!(send.access_count) as u32, disabled: send.disabled.unwrap_or(false), @@ -711,6 +791,27 @@ impl From for SendFileModel { } } +impl From for bitwarden_api_api::models::SendEncryptionType { + fn from(t: SendEncryptionType) -> Self { + match t { + SendEncryptionType::V1 => bitwarden_api_api::models::SendEncryptionType::V1, + } + } +} + +impl TryFrom for SendEncryptionType { + type Error = bitwarden_core::MissingFieldError; + + fn try_from(value: bitwarden_api_api::models::SendEncryptionType) -> Result { + Ok(match value { + bitwarden_api_api::models::SendEncryptionType::V1 => SendEncryptionType::V1, + bitwarden_api_api::models::SendEncryptionType::__Unknown(_) => { + return Err(bitwarden_core::MissingFieldError("encryption_version")); + } + }) + } +} + impl From for SendTextModel { fn from(text: SendText) -> Self { SendTextModel { @@ -744,6 +845,26 @@ impl TryFrom for SendText { } } +impl TryFrom for SendItem { + type Error = SendParseError; + + fn try_from(data: SendDataModel) -> Result { + let cipher = serde_json::from_str::(data.data.unwrap_or("{}".to_string()).as_str()); + match cipher { + Err(_e) => Err(SendParseError::DeserializationFailure( + SendItemDeserializationFailure, + )), + Ok(c) => Ok(SendItem { + encryption_version: SendEncryptionType::try_from( + data.encryption_version + .unwrap_or(SendEncryptionType::V1.into()), + )?, + data: c, + }), + } + } +} + #[cfg(test)] mod tests { use bitwarden_core::key_management::create_test_crypto_with_user_key; @@ -790,6 +911,7 @@ mod tests { text: "2.2VPyLzk1tMLug0X3x7RkaQ==|mrMt9vbZsCJhJIj4eebKyg==|aZ7JeyndytEMR1+uEBupEvaZuUE69D/ejhfdJL8oKq0=".parse().ok(), hidden: false, }), + data: None, key: "2.KLv/j0V4Ebs0dwyPdtt4vw==|jcrFuNYN1Qb3onBlwvtxUV/KpdnR1LPRL4EsCoXNAt4=|gHSywGy4Rj/RsCIZFwze4s2AACYKBtqDXTrQXjkgtIE=".parse().unwrap(), max_access_count: None, access_count: 0, @@ -819,6 +941,7 @@ mod tests { text: Some("This is a test".to_owned()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -852,6 +975,7 @@ mod tests { text: Some("This is a test".to_owned()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -889,6 +1013,7 @@ mod tests { text: Some("This is a test".to_owned()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -929,6 +1054,7 @@ mod tests { text: Some("This is a test".to_owned()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -973,6 +1099,7 @@ mod tests { text: Some("This is a test".to_owned()), hidden: false, }), + data: None, max_access_count: None, access_count: 0, disabled: false, @@ -1032,6 +1159,7 @@ mod tests { text: Some(text_value.parse().unwrap()), hidden: true, }), + data: None, max_access_count: Some(42), access_count: 0, disabled: true, diff --git a/crates/bitwarden-user-crypto-management/src/key_rotation/data.rs b/crates/bitwarden-user-crypto-management/src/key_rotation/data.rs index 290519cfb..17597eaae 100644 --- a/crates/bitwarden-user-crypto-management/src/key_rotation/data.rs +++ b/crates/bitwarden-user-crypto-management/src/key_rotation/data.rs @@ -507,6 +507,7 @@ mod tests { text: Some("This is a test send".to_string()), hidden: false, }), + data: None, r#type: bitwarden_send::SendType::Text, max_access_count: None, access_count: 0, diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index dd7cbc8fa..4344c4801 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -65,7 +65,7 @@ impl Attachment { } #[allow(missing_docs)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index 5c4b5b595..7d70e3b09 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -25,7 +25,7 @@ pub struct Card { } #[allow(missing_docs)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 2fb23f0b8..eaae9a380 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -427,7 +427,7 @@ impl TryFrom for CipherRequestModel { } #[allow(missing_docs)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index 9a16d2c35..6c47b79bb 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -63,7 +63,7 @@ pub struct Field { } #[allow(missing_docs)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index 47b7937bf..6f708a5ae 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -37,7 +37,7 @@ pub struct Identity { } #[allow(missing_docs)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] diff --git a/crates/bitwarden-vault/src/cipher/secure_note.rs b/crates/bitwarden-vault/src/cipher/secure_note.rs index 87e7a4173..2687ad416 100644 --- a/crates/bitwarden-vault/src/cipher/secure_note.rs +++ b/crates/bitwarden-vault/src/cipher/secure_note.rs @@ -52,7 +52,7 @@ impl<'de> Deserialize<'de> for SecureNoteType { } #[allow(missing_docs)] -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index f4abf9015..e51a50699 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -28,7 +28,7 @@ pub struct SshKey { } #[allow(missing_docs)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index f74dbcd30..75ff914fa 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -25,7 +25,7 @@ pub struct PasswordHistory { } #[allow(missing_docs)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] #[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] diff --git a/crates/bw/src/tools/send.rs b/crates/bw/src/tools/send.rs index d75885964..af3c20973 100644 --- a/crates/bw/src/tools/send.rs +++ b/crates/bw/src/tools/send.rs @@ -687,6 +687,8 @@ impl From for SendView { r#type: input.r#type, file: input.file, text: input.text, + // TODO - Use the `data` field when implementing item-type Send support + data: None, max_access_count: input.max_access_count, access_count: input.access_count, disabled: input.disabled, @@ -784,14 +786,17 @@ fn build_create_request(inputs: CreateInputs) -> color_eyre::eyre::Result n, (None, SendViewType::File(f)) => f.file_name.clone(), (None, SendViewType::Text(_)) => { return Err(eyre!("--name is required for text Sends.")); } + (None, SendViewType::Item(_)) => { + return Err(eyre!("--name is required for item Sends.")); + } }; let auth = build_auth(password, emails.as_deref())?; @@ -1814,6 +1819,7 @@ mod tests { text: Some("existing text".to_string()), hidden: false, }), + data: None, max_access_count: Some(42), access_count: 0, disabled: false, @@ -2087,6 +2093,7 @@ mod tests { text: Some("body".to_string()), hidden, }), + data: None, max_access_count: max, access_count: 0, disabled: false,