From 1a92de231a8caeaa4f0b70eb17efb6bb656d34f1 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 29 Jun 2023 15:40:16 -0400 Subject: [PATCH 1/9] Do not consume decrypted struct --- crates/bitwarden/src/crypto.rs | 19 +++++++++---------- crates/bitwarden/src/platform/folders/list.rs | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/crates/bitwarden/src/crypto.rs b/crates/bitwarden/src/crypto.rs index fc9967053..478cca6ef 100644 --- a/crates/bitwarden/src/crypto.rs +++ b/crates/bitwarden/src/crypto.rs @@ -347,7 +347,7 @@ pub trait Encryptable { } pub trait Decryptable { - fn decrypt(self, enc: &EncryptionSettings, org_id: &Option) -> Result; + fn decrypt(&self, enc: &EncryptionSettings, org_id: &Option) -> Result; } impl Encryptable for String { @@ -357,8 +357,8 @@ impl Encryptable for String { } impl Decryptable for CipherString { - fn decrypt(self, enc: &EncryptionSettings, org_id: &Option) -> Result { - enc.decrypt(&self, org_id) + fn decrypt(&self, enc: &EncryptionSettings, org_id: &Option) -> Result { + enc.decrypt(self, org_id) } } @@ -369,8 +369,8 @@ impl, Output> Encryptable> for Option { } impl, Output> Decryptable> for Option { - fn decrypt(self, enc: &EncryptionSettings, org_id: &Option) -> Result> { - self.map(|e| e.decrypt(enc, org_id)).transpose() + fn decrypt(&self, enc: &EncryptionSettings, org_id: &Option) -> Result> { + self.as_ref().map(|e| e.decrypt(enc, org_id)).transpose() } } @@ -381,7 +381,7 @@ impl, Output> Encryptable> for Vec { } impl, Output> Decryptable> for Vec { - fn decrypt(self, enc: &EncryptionSettings, org_id: &Option) -> Result> { + fn decrypt(&self, enc: &EncryptionSettings, org_id: &Option) -> Result> { self.into_iter().map(|e| e.decrypt(enc, org_id)).collect() } } @@ -400,16 +400,15 @@ impl, Output, Id: Hash + Eq> Encryptable, Output, Id: Hash + Eq> Decryptable> +impl, Output, Id: Hash + Eq + Clone> Decryptable> for HashMap { fn decrypt( - self, + &self, enc: &EncryptionSettings, org_id: &Option, ) -> Result> { - self.into_iter() - .map(|(id, e)| Ok((id, e.decrypt(enc, org_id)?))) + self.into_iter().map(|(id, e)| Ok(((id.to_owned()), e.decrypt(enc, org_id)?))) .collect::>>() } } diff --git a/crates/bitwarden/src/platform/folders/list.rs b/crates/bitwarden/src/platform/folders/list.rs index 1445eb353..01a8cf846 100644 --- a/crates/bitwarden/src/platform/folders/list.rs +++ b/crates/bitwarden/src/platform/folders/list.rs @@ -51,7 +51,7 @@ pub struct FolderView { } impl Decryptable for domain::Folder { - fn decrypt(self, enc: &EncryptionSettings, _: &Option) -> Result { + fn decrypt(&self, enc: &EncryptionSettings, _: &Option) -> Result { Ok(FolderView { id: self.id, name: self.name.decrypt(enc, &None)?, From 3a4246b5ab226201f1374aca780c68f96afe15f3 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 29 Jun 2023 16:15:58 -0400 Subject: [PATCH 2/9] Move folders to vault team --- crates/bitwarden-json/src/command.rs | 6 ++++-- crates/bitwarden/src/lib.rs | 2 ++ crates/bitwarden/src/platform/mod.rs | 2 -- crates/bitwarden/src/{platform => vault}/client_folders.rs | 0 crates/bitwarden/src/{platform => vault}/folders/create.rs | 0 crates/bitwarden/src/{platform => vault}/folders/delete.rs | 0 crates/bitwarden/src/{platform => vault}/folders/list.rs | 0 crates/bitwarden/src/{platform => vault}/folders/mod.rs | 0 crates/bitwarden/src/{platform => vault}/folders/update.rs | 0 crates/bitwarden/src/vault/mod.rs | 2 ++ crates/sdk-schemas/src/main.rs | 2 +- 11 files changed, 9 insertions(+), 5 deletions(-) rename crates/bitwarden/src/{platform => vault}/client_folders.rs (100%) rename crates/bitwarden/src/{platform => vault}/folders/create.rs (100%) rename crates/bitwarden/src/{platform => vault}/folders/delete.rs (100%) rename crates/bitwarden/src/{platform => vault}/folders/list.rs (100%) rename crates/bitwarden/src/{platform => vault}/folders/mod.rs (100%) rename crates/bitwarden/src/{platform => vault}/folders/update.rs (100%) create mode 100644 crates/bitwarden/src/vault/mod.rs diff --git a/crates/bitwarden-json/src/command.rs b/crates/bitwarden-json/src/command.rs index 4d3394738..020434138 100644 --- a/crates/bitwarden-json/src/command.rs +++ b/crates/bitwarden-json/src/command.rs @@ -17,10 +17,12 @@ use bitwarden::{ #[cfg(feature = "internal")] use bitwarden::{ auth::request::{ApiKeyLoginRequest, PasswordLoginRequest, SessionLoginRequest}, - platform::{ + vault::{ folders::{FolderCreateRequest, FolderDeleteRequest, FolderUpdateRequest}, - EmptyRequest, FingerprintRequest, SecretVerificationRequest, SyncRequest, }, + platform::{ + EmptyRequest, FingerprintRequest, SecretVerificationRequest, SyncRequest + } }; #[derive(Serialize, Deserialize, JsonSchema, Debug)] diff --git a/crates/bitwarden/src/lib.rs b/crates/bitwarden/src/lib.rs index 161aa0466..9e0751156 100644 --- a/crates/bitwarden/src/lib.rs +++ b/crates/bitwarden/src/lib.rs @@ -55,6 +55,8 @@ pub mod crypto; pub mod error; #[cfg(feature = "internal")] pub mod platform; +#[cfg(feature = "internal")] +pub mod vault; pub mod secrets_manager; pub(crate) mod state; mod util; diff --git a/crates/bitwarden/src/platform/mod.rs b/crates/bitwarden/src/platform/mod.rs index 146472465..a049faf55 100644 --- a/crates/bitwarden/src/platform/mod.rs +++ b/crates/bitwarden/src/platform/mod.rs @@ -1,6 +1,4 @@ -mod client_folders; mod empty_request; -pub mod folders; mod generate_fingerprint; mod get_user_api_key; mod secret_verification_request; diff --git a/crates/bitwarden/src/platform/client_folders.rs b/crates/bitwarden/src/vault/client_folders.rs similarity index 100% rename from crates/bitwarden/src/platform/client_folders.rs rename to crates/bitwarden/src/vault/client_folders.rs diff --git a/crates/bitwarden/src/platform/folders/create.rs b/crates/bitwarden/src/vault/folders/create.rs similarity index 100% rename from crates/bitwarden/src/platform/folders/create.rs rename to crates/bitwarden/src/vault/folders/create.rs diff --git a/crates/bitwarden/src/platform/folders/delete.rs b/crates/bitwarden/src/vault/folders/delete.rs similarity index 100% rename from crates/bitwarden/src/platform/folders/delete.rs rename to crates/bitwarden/src/vault/folders/delete.rs diff --git a/crates/bitwarden/src/platform/folders/list.rs b/crates/bitwarden/src/vault/folders/list.rs similarity index 100% rename from crates/bitwarden/src/platform/folders/list.rs rename to crates/bitwarden/src/vault/folders/list.rs diff --git a/crates/bitwarden/src/platform/folders/mod.rs b/crates/bitwarden/src/vault/folders/mod.rs similarity index 100% rename from crates/bitwarden/src/platform/folders/mod.rs rename to crates/bitwarden/src/vault/folders/mod.rs diff --git a/crates/bitwarden/src/platform/folders/update.rs b/crates/bitwarden/src/vault/folders/update.rs similarity index 100% rename from crates/bitwarden/src/platform/folders/update.rs rename to crates/bitwarden/src/vault/folders/update.rs diff --git a/crates/bitwarden/src/vault/mod.rs b/crates/bitwarden/src/vault/mod.rs new file mode 100644 index 000000000..7b7ab4f89 --- /dev/null +++ b/crates/bitwarden/src/vault/mod.rs @@ -0,0 +1,2 @@ +mod client_folders; +pub mod folders; diff --git a/crates/sdk-schemas/src/main.rs b/crates/sdk-schemas/src/main.rs index 009dcca1d..183401405 100644 --- a/crates/sdk-schemas/src/main.rs +++ b/crates/sdk-schemas/src/main.rs @@ -107,7 +107,7 @@ fn main() -> Result<()> { // Same as above, but for the internal feature #[cfg(feature = "internal")] write_schema_for_response! { - bitwarden::platform::UserApiKeyResponse, + bitwarden::vault::UserApiKeyResponse, }; Ok(()) From a71821f08e9e73241610f9cdfa3b9d14225b4e41 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 29 Jun 2023 18:04:15 -0400 Subject: [PATCH 3/9] Implement typestate and commands for folders --- crates/bitwarden-json/src/client.rs | 5 +- crates/bitwarden-json/src/command.rs | 13 +- crates/bitwarden/src/lib.rs | 1 - crates/bitwarden/src/state/domain.rs | 47 +---- crates/bitwarden/src/state/state.rs | 24 +-- crates/bitwarden/src/state/state_service.rs | 7 +- crates/bitwarden/src/vault/client_folders.rs | 9 +- crates/bitwarden/src/vault/folders/create.rs | 48 ++--- crates/bitwarden/src/vault/folders/delete.rs | 26 +-- crates/bitwarden/src/vault/folders/folder.rs | 191 ++++++++++++++++++ .../src/vault/folders/folder_view.rs | 25 +++ crates/bitwarden/src/vault/folders/get.rs | 36 ++++ crates/bitwarden/src/vault/folders/list.rs | 55 +---- crates/bitwarden/src/vault/folders/mod.rs | 9 +- crates/bitwarden/src/vault/folders/update.rs | 52 ++--- 15 files changed, 334 insertions(+), 214 deletions(-) create mode 100644 crates/bitwarden/src/vault/folders/folder.rs create mode 100644 crates/bitwarden/src/vault/folders/folder_view.rs create mode 100644 crates/bitwarden/src/vault/folders/get.rs diff --git a/crates/bitwarden-json/src/client.rs b/crates/bitwarden-json/src/client.rs index 3fea79dfb..e7eeb646d 100644 --- a/crates/bitwarden-json/src/client.rs +++ b/crates/bitwarden-json/src/client.rs @@ -1,13 +1,12 @@ use bitwarden::client::client_settings::ClientSettings; +#[cfg(feature = "internal")] +use crate::command::FoldersCommand; use crate::{ command::{Command, ProjectsCommand, SecretsCommand}, response::ResponseIntoString, }; -#[cfg(feature = "internal")] -use crate::command::FoldersCommand; - pub struct Client(bitwarden::Client); impl Client { diff --git a/crates/bitwarden-json/src/command.rs b/crates/bitwarden-json/src/command.rs index 020434138..774bcb307 100644 --- a/crates/bitwarden-json/src/command.rs +++ b/crates/bitwarden-json/src/command.rs @@ -1,6 +1,3 @@ -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - use bitwarden::{ auth::request::AccessTokenLoginRequest, secrets_manager::{ @@ -17,13 +14,11 @@ use bitwarden::{ #[cfg(feature = "internal")] use bitwarden::{ auth::request::{ApiKeyLoginRequest, PasswordLoginRequest, SessionLoginRequest}, - vault::{ - folders::{FolderCreateRequest, FolderDeleteRequest, FolderUpdateRequest}, - }, - platform::{ - EmptyRequest, FingerprintRequest, SecretVerificationRequest, SyncRequest - } + platform::{EmptyRequest, FingerprintRequest, SecretVerificationRequest, SyncRequest}, + vault::folders::{FolderCreateRequest, FolderDeleteRequest, FolderUpdateRequest}, }; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, JsonSchema, Debug)] #[serde(rename_all = "camelCase", deny_unknown_fields)] diff --git a/crates/bitwarden/src/lib.rs b/crates/bitwarden/src/lib.rs index 9e0751156..445f66bb4 100644 --- a/crates/bitwarden/src/lib.rs +++ b/crates/bitwarden/src/lib.rs @@ -55,7 +55,6 @@ pub mod crypto; pub mod error; #[cfg(feature = "internal")] pub mod platform; -#[cfg(feature = "internal")] pub mod vault; pub mod secrets_manager; pub(crate) mod state; diff --git a/crates/bitwarden/src/state/domain.rs b/crates/bitwarden/src/state/domain.rs index d87064756..516e59b3d 100644 --- a/crates/bitwarden/src/state/domain.rs +++ b/crates/bitwarden/src/state/domain.rs @@ -1,17 +1,12 @@ -use std::collections::HashMap; - -use std::str::FromStr; - -use chrono::DateTime; -use chrono::Utc; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use uuid::Uuid; +use std::{collections::HashMap, str::FromStr}; use bitwarden_api_api::models::{ - CipherDetailsResponseModel, FolderResponseModel, ProfileResponseModel, + CipherDetailsResponseModel, ProfileResponseModel, }; +use chrono::{DateTime, Utc}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; use crate::{ client::{auth_settings::AuthSettings, LoginMethod}, @@ -39,7 +34,7 @@ pub struct AccountData { pub profile: Option, pub ciphers: HashMap, - pub folders: HashMap, + // pub folders: HashMap, pub settings: Settings, pub auth: Auth, @@ -93,15 +88,6 @@ pub struct Cipher { pub revision_date: DateTime, } -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "camelCase")] -pub struct Folder { - pub id: Uuid, - pub name: CipherString, - - pub revision_date: DateTime, -} - pub(crate) fn convert_cipher(c: CipherDetailsResponseModel) -> Result<(Uuid, Cipher)> { Ok(( c.id.ok_or(Error::MissingFields)?, @@ -133,25 +119,6 @@ pub(crate) fn convert_cipher(c: CipherDetailsResponseModel) -> Result<(Uuid, Cip )) } -pub(crate) fn convert_folder(f: FolderResponseModel) -> Result<(Uuid, Folder)> { - Ok(( - f.id.ok_or(Error::MissingFields)?, - Folder { - id: f.id.ok_or(Error::MissingFields)?, - name: f - .name - .ok_or(Error::MissingFields)? - .parse() - .map_err(|_| Error::InvalidResponse)?, - revision_date: f - .revision_date - .ok_or(Error::MissingFields)? - .parse() - .map_err(|_| Error::InvalidResponse)?, - }, - )) -} - pub(crate) fn convert_keys(profile: &ProfileResponseModel) -> Result { Ok(Keys { crypto_symmetric_key: profile diff --git a/crates/bitwarden/src/state/state.rs b/crates/bitwarden/src/state/state.rs index f940a0220..6de65360f 100644 --- a/crates/bitwarden/src/state/state.rs +++ b/crates/bitwarden/src/state/state.rs @@ -5,14 +5,13 @@ use bitwarden_api_api::models::SyncResponseModel; use serde_json::Value; use uuid::Uuid; +use super::{ + domain::{convert_cipher, convert_keys, convert_profile}, + state_service::{CIPHERS_SERVICE, KEYS_SERVICE, PROFILE_SERVICE}, +}; use crate::{ client::client_settings::ClientSettings, - error::{Error, Result}, -}; - -use super::{ - domain::{convert_cipher, convert_folder, convert_keys, convert_profile}, - state_service::{CIPHERS_SERVICE, FOLDERS_SERVICE, KEYS_SERVICE, PROFILE_SERVICE}, + error::{Error, Result}, vault::folders::store_folders_from_sync, }; pub struct State { @@ -80,18 +79,7 @@ impl State { Ok(()) }) .await?; - self.get_state_service(FOLDERS_SERVICE) - .modify(|f| { - *f = data - .folders - .unwrap_or_default() - .into_iter() - .map(convert_folder) - .collect::>()?; - Ok(()) - }) - .await?; - + store_folders_from_sync(data.folders.unwrap_or_default(), self).await?; Ok(()) } diff --git a/crates/bitwarden/src/state/state_service.rs b/crates/bitwarden/src/state/state_service.rs index def34a841..082b33f58 100644 --- a/crates/bitwarden/src/state/state_service.rs +++ b/crates/bitwarden/src/state/state_service.rs @@ -3,9 +3,8 @@ use std::{collections::HashMap, marker::PhantomData}; use serde::{de::DeserializeOwned, Serialize}; use uuid::Uuid; -use crate::error::Result; - use super::{domain::*, state::State}; +use crate::error::Result; #[derive(Clone, Copy)] pub(crate) struct ServiceDefinition { @@ -16,7 +15,7 @@ pub(crate) struct ServiceDefinition { } impl ServiceDefinition { - const fn new(namespace: &'static str) -> Self { + pub const fn new(namespace: &'static str) -> Self { let _type = PhantomData; Self { namespace, _type } } @@ -27,8 +26,6 @@ pub(crate) const PROFILE_SERVICE: ServiceDefinition> = ServiceDefinition::new("profile"); pub(crate) const CIPHERS_SERVICE: ServiceDefinition> = ServiceDefinition::new("ciphers"); -pub(crate) const FOLDERS_SERVICE: ServiceDefinition> = - ServiceDefinition::new("folders"); pub(crate) const AUTH_SERVICE: ServiceDefinition = ServiceDefinition::new("auth"); pub(crate) const SETTINGS_SERVICE: ServiceDefinition = ServiceDefinition::new("settings"); diff --git a/crates/bitwarden/src/vault/client_folders.rs b/crates/bitwarden/src/vault/client_folders.rs index 0dadea631..9b5edfb50 100644 --- a/crates/bitwarden/src/vault/client_folders.rs +++ b/crates/bitwarden/src/vault/client_folders.rs @@ -1,9 +1,8 @@ -use crate::{error::Result, Client}; - use super::folders::{ create_folder, delete_folder, list_folders, update_folder, FolderCreateRequest, - FolderDeleteRequest, FolderUpdateRequest, FoldersResponse, + FolderDeleteRequest, FolderUpdateRequest, FoldersResponse, FolderGetRequest, get_folder, FolderResponse, }; +use crate::{error::Result, Client}; pub struct ClientFolders<'a> { pub(crate) client: &'a mut crate::Client, @@ -14,6 +13,10 @@ impl<'a> ClientFolders<'a> { create_folder(self.client, input).await } + pub async fn get(&self, input: FolderGetRequest) -> Result { + get_folder(self.client, input).await + } + pub async fn list(&self) -> Result { list_folders(self.client).await } diff --git a/crates/bitwarden/src/vault/folders/create.rs b/crates/bitwarden/src/vault/folders/create.rs index afed93ef5..1b4fff364 100644 --- a/crates/bitwarden/src/vault/folders/create.rs +++ b/crates/bitwarden/src/vault/folders/create.rs @@ -1,11 +1,13 @@ use bitwarden_api_api::models::FolderRequestModel; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use uuid::Uuid; +use super::FolderToSave; use crate::{ + client::encryption_settings::EncryptionSettings, crypto::Encryptable, error::{Error, Result}, - state::{domain::Folder, state_service::FOLDERS_SERVICE}, Client, }; @@ -16,40 +18,18 @@ pub struct FolderCreateRequest { pub name: String, } -pub(crate) async fn create_folder(client: &mut Client, input: FolderCreateRequest) -> Result<()> { - let enc = client - .get_encryption_settings() - .as_ref() - .ok_or(Error::VaultLocked)?; - - let name = input.name.encrypt(&enc, &None)?; - - let param = Some(FolderRequestModel { - name: name.to_string(), - }); - - let config = client.get_api_configurations().await; - let res = bitwarden_api_api::apis::folders_api::folders_post(&config.api, param).await?; - - client - .get_state_service(FOLDERS_SERVICE) - .modify(move |folders| { - let id = res.id.unwrap(); - folders.insert( - id, - Folder { - id, - name, - revision_date: res - .revision_date - .unwrap() - .parse() - .map_err(|_| Error::InvalidResponse)?, - }, - ); - Ok(()) +impl Encryptable for FolderCreateRequest { + fn encrypt(self, enc: &EncryptionSettings, _: &Option) -> Result { + Ok(FolderToSave { + id: None, + name: enc.encrypt(&self.name.as_bytes(), &None)?, }) - .await?; + } +} + +pub(crate) async fn create_folder(client: &mut Client, input: FolderCreateRequest) -> Result<()> { + let enc = client.get_encryption_settings().as_ref().ok_or(Error::VaultLocked)?; + input.encrypt(enc, &None)?.save_to_server(client).await?; Ok(()) } diff --git a/crates/bitwarden/src/vault/folders/delete.rs b/crates/bitwarden/src/vault/folders/delete.rs index 5ddcb1e84..7de9ccdae 100644 --- a/crates/bitwarden/src/vault/folders/delete.rs +++ b/crates/bitwarden/src/vault/folders/delete.rs @@ -2,7 +2,9 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::{error::Result, state::state_service::FOLDERS_SERVICE, Client}; +use crate::{error::Result, Client}; + +use super::folder::FolderToDelete; #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -11,17 +13,15 @@ pub struct FolderDeleteRequest { pub id: Uuid, } -pub(crate) async fn delete_folder(client: &mut Client, input: FolderDeleteRequest) -> Result<()> { - let config: &crate::client::ApiConfigurations = client.get_api_configurations().await; - bitwarden_api_api::apis::folders_api::folders_id_delete(&config.api, &input.id.to_string()) - .await?; +impl From for FolderToDelete { + fn from(input: FolderDeleteRequest) -> Self { + Self { + id: input.id, + } + } +} - client - .get_state_service(FOLDERS_SERVICE) - .modify(move |folders| { - folders.remove(&input.id); - Ok(()) - }) - .await?; - Ok(()) +pub(crate) async fn delete_folder(client: &mut Client, input: FolderDeleteRequest) -> Result<()> { + let input: FolderToDelete = input.into(); + Ok(input.delete_from_server(client).await?) } diff --git a/crates/bitwarden/src/vault/folders/folder.rs b/crates/bitwarden/src/vault/folders/folder.rs new file mode 100644 index 000000000..67af007d1 --- /dev/null +++ b/crates/bitwarden/src/vault/folders/folder.rs @@ -0,0 +1,191 @@ +use std::collections::HashMap; + +use bitwarden_api_api::models::{FolderRequestModel, FolderResponseModel}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::{ + crypto::CipherString, + error::{Error, Result}, + state::{state_service::ServiceDefinition, state::State}, + Client, +}; + +/// Storage service for folders. Applies the `folders` namespace to stored items. +/// Private to enable tighter control over Folder state +const FOLDERS_SERVICE: ServiceDefinition> = ServiceDefinition::new("folders"); + +/// Folder is the root struct representing a Folder as it is stored on disk and communicated from the server +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Folder { + pub id: Uuid, + pub name: CipherString, + pub revision_date: DateTime, +} + +impl TryFrom for Folder { + type Error = Error; + + fn try_from(value: FolderResponseModel) -> Result { + Ok(Folder { + id: value.id.ok_or(Error::MissingFields)?, + name: value + .name + .ok_or(Error::MissingFields)? + .parse() + .map_err(|_| Error::InvalidResponse)?, + revision_date: value + .revision_date + .ok_or(Error::MissingFields)? + .parse() + .map_err(|_| Error::InvalidResponse)?, + }) + } +} + +/// Struct representing a Folder that has been loaded from disk +#[derive(Debug, Clone)] +pub struct FolderFromDisk(Folder); + +impl FolderFromDisk { + pub fn id(&self) -> &Uuid { + &self.0.id + } + + pub fn name(&self) -> &CipherString { + &self.0.name + } + + pub fn revision_date(&self) -> &DateTime { + &self.0.revision_date + } + + pub async fn list(client: &Client) -> Vec { + client + .get_state_service(FOLDERS_SERVICE) + .get() + .await + .into_iter() + .map(|f| FolderFromDisk(f.1)) + .collect() + } + + pub async fn get(id: Uuid, client: &Client) -> Option { + client + .get_state_service(FOLDERS_SERVICE) + .get() + .await + .into_iter() + .find(|f| f.0 == id) + .map(|f| FolderFromDisk(f.1)) + } +} + +/// Struct representing a folder view that has been encrypted and must be stored on the server and on disk +#[derive(Debug, Clone)] +pub struct FolderToSave { + pub id: Option, + pub name: CipherString, +} + +impl FolderToSave { + pub async fn save_to_server(self, client: &mut Client) -> Result { + let config = client.get_api_configurations().await; + + let request = Some(FolderRequestModel::new(self.name.to_string())); + + let res = match self.id { + Some(id) => { + bitwarden_api_api::apis::folders_api::folders_id_put( + &config.api, + &id.to_string(), + request, + ) + .await? + } + None => { + bitwarden_api_api::apis::folders_api::folders_post(&config.api, request).await? + } + }; + + Ok(store_folder_response(res, client).await?) + } +} + +#[derive(Debug, Clone)] +pub struct FolderToDelete { + pub id: Uuid, +} + +impl FolderToDelete { + pub async fn delete_from_server(self, client: &mut Client) -> Result<()> { + let config = client.get_api_configurations().await; + + bitwarden_api_api::apis::folders_api::folders_id_delete(&config.api, &self.id.to_string()) + .await?; + + client + .get_state_service(FOLDERS_SERVICE) + .modify(|folders| { + folders.remove(&self.id); + Ok(()) + }) + .await?; + + Ok(remove_folder(self.id, client).await?) + } +} + +/// Processes a FolderResponseModel and stores it on disk +async fn store_folder_response( + response: FolderResponseModel, + client: &Client, +) -> Result { + let folder = response.try_into()?; + + // TODO store folder on disk + + Ok(FolderFromDisk(folder)) +} + +/// Removes a folder from disk +async fn remove_folder(id: Uuid, client: &Client) -> Result<()> { + client + .get_state_service(FOLDERS_SERVICE) + .modify(|folders| { + folders.remove(&id); + Ok(()) + }) + .await?; + + Ok(()) +} + +/// Clobbers folders list with given folder response values +/// used during sync events +pub async fn store_folders_from_sync(folders: Vec, state: &State) -> Result<()> { + let folders: HashMap = prep_folders_for_storage(folders)?; + + state.get_state_service(FOLDERS_SERVICE).modify(|f| { + *f = folders; + Ok(()) + }).await?; + Ok(()) +} + + +/// Processes a list of FolderResponseModels into [Folders](Folder). +/// this is used during [sync](crate::commands::sync::sync) to update the full list of folders for an account. +fn prep_folders_for_storage( + folders: Vec, +) -> Result> { + folders + .into_iter() + .map(|r| { + let folder: Folder = r.try_into()?; + Ok((folder.id, folder)) + }) + .collect() +} diff --git a/crates/bitwarden/src/vault/folders/folder_view.rs b/crates/bitwarden/src/vault/folders/folder_view.rs new file mode 100644 index 000000000..27ab1a8cd --- /dev/null +++ b/crates/bitwarden/src/vault/folders/folder_view.rs @@ -0,0 +1,25 @@ +use chrono::{DateTime, Utc}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use super::folder::{FolderFromDisk,}; +use crate::{client::encryption_settings::EncryptionSettings, crypto::{Decryptable}, error::{Result, Error}, Client}; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct FolderView { + id: Uuid, + name: String, + revision_date: DateTime, +} + +impl Decryptable for FolderFromDisk { + fn decrypt(&self, enc: &EncryptionSettings, _: &Option) -> Result { + Ok(FolderView { + id: self.id().to_owned(), + name: enc.decrypt(&self.name(), &None)?, + revision_date: self.revision_date().clone(), + }) + } +} diff --git a/crates/bitwarden/src/vault/folders/get.rs b/crates/bitwarden/src/vault/folders/get.rs new file mode 100644 index 000000000..f877d6848 --- /dev/null +++ b/crates/bitwarden/src/vault/folders/get.rs @@ -0,0 +1,36 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use super::{FolderFromDisk, FolderView}; +use crate::{ + crypto::Decryptable, + error::{Error, Result}, + Client, +}; + +#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct FolderGetRequest { + /// Folder id to get + pub id: Uuid, +} + +pub(crate) async fn get_folder(client: &Client, input: FolderGetRequest) -> Result { + let enc = client + .get_encryption_settings() + .as_ref() + .ok_or(Error::VaultLocked)?; + + let folder = FolderFromDisk::get(input.id, client) + .await + .map(|f| f.decrypt(enc, &None)).transpose()?; + + Ok(FolderResponse { data: folder }) +} + +#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct FolderResponse { + pub data: Option, +} diff --git a/crates/bitwarden/src/vault/folders/list.rs b/crates/bitwarden/src/vault/folders/list.rs index 01a8cf846..872f95b13 100644 --- a/crates/bitwarden/src/vault/folders/list.rs +++ b/crates/bitwarden/src/vault/folders/list.rs @@ -1,38 +1,26 @@ -use chrono::{DateTime, Utc}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use uuid::Uuid; +use super::{FolderFromDisk, FolderView}; use crate::{ - client::encryption_settings::EncryptionSettings, - crypto::{Decryptable, Encryptable}, + crypto::Decryptable, error::{Error, Result}, - state::{domain, state_service::FOLDERS_SERVICE}, Client, }; -#[derive(Serialize, Deserialize, Debug, JsonSchema)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct FolderCreateRequest { - /// Encrypted folder name - pub name: String, -} - pub(crate) async fn list_folders(client: &Client) -> Result { let enc = client .get_encryption_settings() .as_ref() .ok_or(Error::VaultLocked)?; - let folders = client - .get_state_service(FOLDERS_SERVICE) - .get() + let folders = FolderFromDisk::list(client) .await - .decrypt(enc, &None)?; + .into_iter() + .map(|f| f.decrypt(enc, &None)) + .collect::>>()?; - Ok(FoldersResponse { - data: folders.into_iter().map(|f| f.1).collect(), - }) + Ok(FoldersResponse { data: folders }) } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -40,32 +28,3 @@ pub(crate) async fn list_folders(client: &Client) -> Result { pub struct FoldersResponse { pub data: Vec, } - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "camelCase")] -pub struct FolderView { - pub id: Uuid, - pub name: String, - - pub revision_date: DateTime, -} - -impl Decryptable for domain::Folder { - fn decrypt(&self, enc: &EncryptionSettings, _: &Option) -> Result { - Ok(FolderView { - id: self.id, - name: self.name.decrypt(enc, &None)?, - revision_date: self.revision_date, - }) - } -} - -impl Encryptable for FolderView { - fn encrypt(self, enc: &EncryptionSettings, _: &Option) -> Result { - Ok(domain::Folder { - id: self.id, - name: self.name.encrypt(enc, &None)?, - revision_date: self.revision_date, - }) - } -} diff --git a/crates/bitwarden/src/vault/folders/mod.rs b/crates/bitwarden/src/vault/folders/mod.rs index 0dd261bb6..9ce1ee570 100644 --- a/crates/bitwarden/src/vault/folders/mod.rs +++ b/crates/bitwarden/src/vault/folders/mod.rs @@ -1,5 +1,8 @@ mod create; mod delete; +mod get; +mod folder; +mod folder_view; mod list; mod update; @@ -7,7 +10,11 @@ pub(crate) use create::create_folder; pub use create::FolderCreateRequest; pub(crate) use delete::delete_folder; pub use delete::FolderDeleteRequest; +pub(crate) use get::get_folder; +pub use get::{FolderGetRequest, FolderResponse}; +pub(crate) use folder::{store_folders_from_sync, FolderFromDisk, FolderToSave}; +pub use folder_view::FolderView; pub(crate) use list::list_folders; -pub use list::{FolderView, FoldersResponse}; +pub use list::FoldersResponse; pub(crate) use update::update_folder; pub use update::FolderUpdateRequest; diff --git a/crates/bitwarden/src/vault/folders/update.rs b/crates/bitwarden/src/vault/folders/update.rs index 7431a2686..c606d0226 100644 --- a/crates/bitwarden/src/vault/folders/update.rs +++ b/crates/bitwarden/src/vault/folders/update.rs @@ -1,12 +1,12 @@ -use bitwarden_api_api::models::FolderRequestModel; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; +use super::FolderToSave; use crate::{ + client::encryption_settings::EncryptionSettings, crypto::Encryptable, error::{Error, Result}, - state::{domain::Folder, state_service::FOLDERS_SERVICE}, Client, }; @@ -20,44 +20,18 @@ pub struct FolderUpdateRequest { pub name: String, } -pub(crate) async fn update_folder(client: &mut Client, input: FolderUpdateRequest) -> Result<()> { - let enc = client - .get_encryption_settings() - .as_ref() - .ok_or(Error::VaultLocked)?; - - let name = input.name.encrypt(&enc, &None)?; - - let param = Some(FolderRequestModel { - name: name.to_string(), - }); +impl Encryptable for FolderUpdateRequest { + fn encrypt(self, enc: &EncryptionSettings, _: &Option) -> Result { + Ok(FolderToSave { + id: Some(self.id), + name: enc.encrypt(&self.name.as_bytes(), &None)?, + }) + } +} - let config = client.get_api_configurations().await; - let res = bitwarden_api_api::apis::folders_api::folders_id_put( - &config.api, - &input.id.to_string(), - param, - ) - .await?; +pub(crate) async fn update_folder(client: &mut Client, input: FolderUpdateRequest) -> Result<()> { + let enc = client.get_encryption_settings().as_ref().ok_or(Error::VaultLocked)?; - client - .get_state_service(FOLDERS_SERVICE) - .modify(move |folders| { - let id = res.id.unwrap(); - folders.insert( - id, - Folder { - id, - name, - revision_date: res - .revision_date - .unwrap() - .parse() - .map_err(|_| Error::InvalidResponse)?, - }, - ); - Ok(()) - }) - .await?; + input.encrypt(enc, &None)?.save_to_server(client).await?; Ok(()) } From c6b34c1897f8bc1fae24b29dd305519714e95509 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 29 Jun 2023 18:04:33 -0400 Subject: [PATCH 4/9] format :robot: --- crates/bitwarden/src/client/client.rs | 1 - .../src/client/encryption_settings.rs | 3 +-- crates/bitwarden/src/crypto.rs | 3 ++- crates/bitwarden/src/lib.rs | 2 +- crates/bitwarden/src/state/domain.rs | 5 +--- crates/bitwarden/src/state/state.rs | 3 ++- crates/bitwarden/src/vault/client_folders.rs | 4 ++-- crates/bitwarden/src/vault/folders/create.rs | 5 +++- crates/bitwarden/src/vault/folders/delete.rs | 7 ++---- crates/bitwarden/src/vault/folders/folder.rs | 23 +++++++++++-------- .../src/vault/folders/folder_view.rs | 9 ++++++-- crates/bitwarden/src/vault/folders/get.rs | 3 ++- crates/bitwarden/src/vault/folders/mod.rs | 6 ++--- crates/bitwarden/src/vault/folders/update.rs | 5 +++- 14 files changed, 44 insertions(+), 35 deletions(-) diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 115d30f67..c869eac7c 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -23,7 +23,6 @@ use crate::{ state_service::{AUTH_SERVICE, KEYS_SERVICE}, }, }; - #[cfg(feature = "internal")] use crate::{ auth::{ diff --git a/crates/bitwarden/src/client/encryption_settings.rs b/crates/bitwarden/src/client/encryption_settings.rs index 6aad2f9f4..3321784c8 100644 --- a/crates/bitwarden/src/client/encryption_settings.rs +++ b/crates/bitwarden/src/client/encryption_settings.rs @@ -272,9 +272,8 @@ fn validate_mac(mac_key: &[u8], iv: &[u8], data: &[u8]) -> Result<[u8; 32]> { mod tests { use std::str::FromStr; - use crate::crypto::{Decryptable, Encryptable}; - use super::{EncryptionSettings, SymmetricCryptoKey}; + use crate::crypto::{Decryptable, Encryptable}; #[test] fn test_symmetric_crypto_key() { diff --git a/crates/bitwarden/src/crypto.rs b/crates/bitwarden/src/crypto.rs index 478cca6ef..80bc92738 100644 --- a/crates/bitwarden/src/crypto.rs +++ b/crates/bitwarden/src/crypto.rs @@ -408,7 +408,8 @@ impl, Output, Id: Hash + Eq + Clone> Decryptable, ) -> Result> { - self.into_iter().map(|(id, e)| Ok(((id.to_owned()), e.decrypt(enc, org_id)?))) + self.into_iter() + .map(|(id, e)| Ok(((id.to_owned()), e.decrypt(enc, org_id)?))) .collect::>>() } } diff --git a/crates/bitwarden/src/lib.rs b/crates/bitwarden/src/lib.rs index 445f66bb4..b0e1cd3d4 100644 --- a/crates/bitwarden/src/lib.rs +++ b/crates/bitwarden/src/lib.rs @@ -55,10 +55,10 @@ pub mod crypto; pub mod error; #[cfg(feature = "internal")] pub mod platform; -pub mod vault; pub mod secrets_manager; pub(crate) mod state; mod util; +pub mod vault; pub mod wordlist; pub use client::Client; diff --git a/crates/bitwarden/src/state/domain.rs b/crates/bitwarden/src/state/domain.rs index 516e59b3d..edbdf092a 100644 --- a/crates/bitwarden/src/state/domain.rs +++ b/crates/bitwarden/src/state/domain.rs @@ -1,8 +1,6 @@ use std::{collections::HashMap, str::FromStr}; -use bitwarden_api_api::models::{ - CipherDetailsResponseModel, ProfileResponseModel, -}; +use bitwarden_api_api::models::{CipherDetailsResponseModel, ProfileResponseModel}; use chrono::{DateTime, Utc}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -35,7 +33,6 @@ pub struct AccountData { pub ciphers: HashMap, // pub folders: HashMap, - pub settings: Settings, pub auth: Auth, } diff --git a/crates/bitwarden/src/state/state.rs b/crates/bitwarden/src/state/state.rs index 6de65360f..e335b139f 100644 --- a/crates/bitwarden/src/state/state.rs +++ b/crates/bitwarden/src/state/state.rs @@ -11,7 +11,8 @@ use super::{ }; use crate::{ client::client_settings::ClientSettings, - error::{Error, Result}, vault::folders::store_folders_from_sync, + error::{Error, Result}, + vault::folders::store_folders_from_sync, }; pub struct State { diff --git a/crates/bitwarden/src/vault/client_folders.rs b/crates/bitwarden/src/vault/client_folders.rs index 9b5edfb50..e84e75156 100644 --- a/crates/bitwarden/src/vault/client_folders.rs +++ b/crates/bitwarden/src/vault/client_folders.rs @@ -1,6 +1,6 @@ use super::folders::{ - create_folder, delete_folder, list_folders, update_folder, FolderCreateRequest, - FolderDeleteRequest, FolderUpdateRequest, FoldersResponse, FolderGetRequest, get_folder, FolderResponse, + create_folder, delete_folder, get_folder, list_folders, update_folder, FolderCreateRequest, + FolderDeleteRequest, FolderGetRequest, FolderResponse, FolderUpdateRequest, FoldersResponse, }; use crate::{error::Result, Client}; diff --git a/crates/bitwarden/src/vault/folders/create.rs b/crates/bitwarden/src/vault/folders/create.rs index 1b4fff364..e18edceb4 100644 --- a/crates/bitwarden/src/vault/folders/create.rs +++ b/crates/bitwarden/src/vault/folders/create.rs @@ -28,7 +28,10 @@ impl Encryptable for FolderCreateRequest { } pub(crate) async fn create_folder(client: &mut Client, input: FolderCreateRequest) -> Result<()> { - let enc = client.get_encryption_settings().as_ref().ok_or(Error::VaultLocked)?; + let enc = client + .get_encryption_settings() + .as_ref() + .ok_or(Error::VaultLocked)?; input.encrypt(enc, &None)?.save_to_server(client).await?; Ok(()) diff --git a/crates/bitwarden/src/vault/folders/delete.rs b/crates/bitwarden/src/vault/folders/delete.rs index 7de9ccdae..4241d7e6d 100644 --- a/crates/bitwarden/src/vault/folders/delete.rs +++ b/crates/bitwarden/src/vault/folders/delete.rs @@ -2,9 +2,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::{error::Result, Client}; - use super::folder::FolderToDelete; +use crate::{error::Result, Client}; #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -15,9 +14,7 @@ pub struct FolderDeleteRequest { impl From for FolderToDelete { fn from(input: FolderDeleteRequest) -> Self { - Self { - id: input.id, - } + Self { id: input.id } } } diff --git a/crates/bitwarden/src/vault/folders/folder.rs b/crates/bitwarden/src/vault/folders/folder.rs index 67af007d1..36c88da72 100644 --- a/crates/bitwarden/src/vault/folders/folder.rs +++ b/crates/bitwarden/src/vault/folders/folder.rs @@ -8,7 +8,7 @@ use uuid::Uuid; use crate::{ crypto::CipherString, error::{Error, Result}, - state::{state_service::ServiceDefinition, state::State}, + state::{state::State, state_service::ServiceDefinition}, Client, }; @@ -165,22 +165,25 @@ async fn remove_folder(id: Uuid, client: &Client) -> Result<()> { /// Clobbers folders list with given folder response values /// used during sync events -pub async fn store_folders_from_sync(folders: Vec, state: &State) -> Result<()> { +pub async fn store_folders_from_sync( + folders: Vec, + state: &State, +) -> Result<()> { let folders: HashMap = prep_folders_for_storage(folders)?; - state.get_state_service(FOLDERS_SERVICE).modify(|f| { - *f = folders; - Ok(()) - }).await?; + state + .get_state_service(FOLDERS_SERVICE) + .modify(|f| { + *f = folders; + Ok(()) + }) + .await?; Ok(()) } - /// Processes a list of FolderResponseModels into [Folders](Folder). /// this is used during [sync](crate::commands::sync::sync) to update the full list of folders for an account. -fn prep_folders_for_storage( - folders: Vec, -) -> Result> { +fn prep_folders_for_storage(folders: Vec) -> Result> { folders .into_iter() .map(|r| { diff --git a/crates/bitwarden/src/vault/folders/folder_view.rs b/crates/bitwarden/src/vault/folders/folder_view.rs index 27ab1a8cd..d841ab7d9 100644 --- a/crates/bitwarden/src/vault/folders/folder_view.rs +++ b/crates/bitwarden/src/vault/folders/folder_view.rs @@ -3,8 +3,13 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::folder::{FolderFromDisk,}; -use crate::{client::encryption_settings::EncryptionSettings, crypto::{Decryptable}, error::{Result, Error}, Client}; +use super::folder::FolderFromDisk; +use crate::{ + client::encryption_settings::EncryptionSettings, + crypto::Decryptable, + error::{Error, Result}, + Client, +}; #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] diff --git a/crates/bitwarden/src/vault/folders/get.rs b/crates/bitwarden/src/vault/folders/get.rs index f877d6848..71b1dd670 100644 --- a/crates/bitwarden/src/vault/folders/get.rs +++ b/crates/bitwarden/src/vault/folders/get.rs @@ -24,7 +24,8 @@ pub(crate) async fn get_folder(client: &Client, input: FolderGetRequest) -> Resu let folder = FolderFromDisk::get(input.id, client) .await - .map(|f| f.decrypt(enc, &None)).transpose()?; + .map(|f| f.decrypt(enc, &None)) + .transpose()?; Ok(FolderResponse { data: folder }) } diff --git a/crates/bitwarden/src/vault/folders/mod.rs b/crates/bitwarden/src/vault/folders/mod.rs index 9ce1ee570..6a7deead0 100644 --- a/crates/bitwarden/src/vault/folders/mod.rs +++ b/crates/bitwarden/src/vault/folders/mod.rs @@ -1,8 +1,8 @@ mod create; mod delete; -mod get; mod folder; mod folder_view; +mod get; mod list; mod update; @@ -10,10 +10,10 @@ pub(crate) use create::create_folder; pub use create::FolderCreateRequest; pub(crate) use delete::delete_folder; pub use delete::FolderDeleteRequest; -pub(crate) use get::get_folder; -pub use get::{FolderGetRequest, FolderResponse}; pub(crate) use folder::{store_folders_from_sync, FolderFromDisk, FolderToSave}; pub use folder_view::FolderView; +pub(crate) use get::get_folder; +pub use get::{FolderGetRequest, FolderResponse}; pub(crate) use list::list_folders; pub use list::FoldersResponse; pub(crate) use update::update_folder; diff --git a/crates/bitwarden/src/vault/folders/update.rs b/crates/bitwarden/src/vault/folders/update.rs index c606d0226..4aecb3ea7 100644 --- a/crates/bitwarden/src/vault/folders/update.rs +++ b/crates/bitwarden/src/vault/folders/update.rs @@ -30,7 +30,10 @@ impl Encryptable for FolderUpdateRequest { } pub(crate) async fn update_folder(client: &mut Client, input: FolderUpdateRequest) -> Result<()> { - let enc = client.get_encryption_settings().as_ref().ok_or(Error::VaultLocked)?; + let enc = client + .get_encryption_settings() + .as_ref() + .ok_or(Error::VaultLocked)?; input.encrypt(enc, &None)?.save_to_server(client).await?; Ok(()) From 6229235e0aa62a959a72e86dfba81782e2e4f96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Mon, 10 Jul 2023 19:01:02 +0200 Subject: [PATCH 5/9] Update schemas --- .../src-ts/bitwarden_client/schemas.ts | 65 ++++++++++ languages/csharp/schemas.cs | 66 ++++++++++ .../bitwarden_client/schemas.ts | 65 ++++++++++ languages/python/BitwardenClient/schemas.py | 117 ++++++++++++++++- support/schemas/bitwarden_json/Command.json | 118 ++++++++++++++++++ 5 files changed, 426 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts b/crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts index 920468756..dca1352ff 100644 --- a/crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts +++ b/crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts @@ -129,6 +129,7 @@ export interface Command { sync?: SyncRequest; secrets?: SecretsCommand; projects?: ProjectsCommand; + folders?: FoldersCommand; } /** @@ -170,6 +171,53 @@ export interface FingerprintRequest { publicKey: string; } +/** + * > Requires Authentication > Requires an unlocked vault Creates a new folder with the + * provided data + * + * > Requires Authentication > Requires an unlocked vault and calling Sync at least once + * Lists all folders in the vault + * + * Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + * + * > Requires Authentication > Requires an unlocked vault Updates an existing folder with + * the provided data given its ID + * + * > Requires Authentication > Requires an unlocked vault Deletes the folder associated with + * the provided ID + */ +export interface FoldersCommand { + create?: FolderCreateRequest; + list?: { [key: string]: any }; + update?: FolderUpdateRequest; + delete?: FolderDeleteRequest; +} + +export interface FolderCreateRequest { + /** + * Encrypted folder name + */ + name: string; +} + +export interface FolderDeleteRequest { + /** + * ID of the folder to delete + */ + id: string; +} + +export interface FolderUpdateRequest { + /** + * ID of the folder to update + */ + id: string; + /** + * Encrypted folder name + */ + name: string; +} + export interface SecretVerificationRequest { /** * The user's master password to use for user verification. If supplied, this will be used @@ -871,6 +919,7 @@ const typeMap: any = { { json: "sync", js: "sync", typ: u(undefined, r("SyncRequest")) }, { json: "secrets", js: "secrets", typ: u(undefined, r("SecretsCommand")) }, { json: "projects", js: "projects", typ: u(undefined, r("ProjectsCommand")) }, + { json: "folders", js: "folders", typ: u(undefined, r("FoldersCommand")) }, ], false), "AccessTokenLoginRequest": o([ { json: "accessToken", js: "accessToken", typ: "" }, @@ -884,6 +933,22 @@ const typeMap: any = { { json: "fingerprintMaterial", js: "fingerprintMaterial", typ: "" }, { json: "publicKey", js: "publicKey", typ: "" }, ], false), + "FoldersCommand": o([ + { json: "create", js: "create", typ: u(undefined, r("FolderCreateRequest")) }, + { json: "list", js: "list", typ: u(undefined, m("any")) }, + { json: "update", js: "update", typ: u(undefined, r("FolderUpdateRequest")) }, + { json: "delete", js: "delete", typ: u(undefined, r("FolderDeleteRequest")) }, + ], false), + "FolderCreateRequest": o([ + { json: "name", js: "name", typ: "" }, + ], false), + "FolderDeleteRequest": o([ + { json: "id", js: "id", typ: "" }, + ], false), + "FolderUpdateRequest": o([ + { json: "id", js: "id", typ: "" }, + { json: "name", js: "name", typ: "" }, + ], false), "SecretVerificationRequest": o([ { json: "masterPassword", js: "masterPassword", typ: u(undefined, u(null, "")) }, { json: "otp", js: "otp", typ: u(undefined, u(null, "")) }, diff --git a/languages/csharp/schemas.cs b/languages/csharp/schemas.cs index 68d6bf8bb..2b869a7b1 100644 --- a/languages/csharp/schemas.cs +++ b/languages/csharp/schemas.cs @@ -137,6 +137,9 @@ public partial class Command [JsonProperty("projects", NullValueHandling = NullValueHandling.Ignore)] public ProjectsCommand Projects { get; set; } + + [JsonProperty("folders", NullValueHandling = NullValueHandling.Ignore)] + public FoldersCommand Folders { get; set; } } /// @@ -190,6 +193,69 @@ public partial class FingerprintRequest public string PublicKey { get; set; } } + /// + /// > Requires Authentication > Requires an unlocked vault Creates a new folder with the + /// provided data + /// + /// > Requires Authentication > Requires an unlocked vault and calling Sync at least once + /// Lists all folders in the vault + /// + /// Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + /// + /// > Requires Authentication > Requires an unlocked vault Updates an existing folder with + /// the provided data given its ID + /// + /// > Requires Authentication > Requires an unlocked vault Deletes the folder associated with + /// the provided ID + /// + public partial class FoldersCommand + { + [JsonProperty("create", NullValueHandling = NullValueHandling.Ignore)] + public FolderCreateRequest Create { get; set; } + + [JsonProperty("list", NullValueHandling = NullValueHandling.Ignore)] + public Dictionary List { get; set; } + + [JsonProperty("update", NullValueHandling = NullValueHandling.Ignore)] + public FolderUpdateRequest Update { get; set; } + + [JsonProperty("delete", NullValueHandling = NullValueHandling.Ignore)] + public FolderDeleteRequest Delete { get; set; } + } + + public partial class FolderCreateRequest + { + /// + /// Encrypted folder name + /// + [JsonProperty("name")] + public string Name { get; set; } + } + + public partial class FolderDeleteRequest + { + /// + /// ID of the folder to delete + /// + [JsonProperty("id")] + public Guid Id { get; set; } + } + + public partial class FolderUpdateRequest + { + /// + /// ID of the folder to update + /// + [JsonProperty("id")] + public Guid Id { get; set; } + + /// + /// Encrypted folder name + /// + [JsonProperty("name")] + public string Name { get; set; } + } + public partial class SecretVerificationRequest { /// diff --git a/languages/js_webassembly/bitwarden_client/schemas.ts b/languages/js_webassembly/bitwarden_client/schemas.ts index 920468756..dca1352ff 100644 --- a/languages/js_webassembly/bitwarden_client/schemas.ts +++ b/languages/js_webassembly/bitwarden_client/schemas.ts @@ -129,6 +129,7 @@ export interface Command { sync?: SyncRequest; secrets?: SecretsCommand; projects?: ProjectsCommand; + folders?: FoldersCommand; } /** @@ -170,6 +171,53 @@ export interface FingerprintRequest { publicKey: string; } +/** + * > Requires Authentication > Requires an unlocked vault Creates a new folder with the + * provided data + * + * > Requires Authentication > Requires an unlocked vault and calling Sync at least once + * Lists all folders in the vault + * + * Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + * + * > Requires Authentication > Requires an unlocked vault Updates an existing folder with + * the provided data given its ID + * + * > Requires Authentication > Requires an unlocked vault Deletes the folder associated with + * the provided ID + */ +export interface FoldersCommand { + create?: FolderCreateRequest; + list?: { [key: string]: any }; + update?: FolderUpdateRequest; + delete?: FolderDeleteRequest; +} + +export interface FolderCreateRequest { + /** + * Encrypted folder name + */ + name: string; +} + +export interface FolderDeleteRequest { + /** + * ID of the folder to delete + */ + id: string; +} + +export interface FolderUpdateRequest { + /** + * ID of the folder to update + */ + id: string; + /** + * Encrypted folder name + */ + name: string; +} + export interface SecretVerificationRequest { /** * The user's master password to use for user verification. If supplied, this will be used @@ -871,6 +919,7 @@ const typeMap: any = { { json: "sync", js: "sync", typ: u(undefined, r("SyncRequest")) }, { json: "secrets", js: "secrets", typ: u(undefined, r("SecretsCommand")) }, { json: "projects", js: "projects", typ: u(undefined, r("ProjectsCommand")) }, + { json: "folders", js: "folders", typ: u(undefined, r("FoldersCommand")) }, ], false), "AccessTokenLoginRequest": o([ { json: "accessToken", js: "accessToken", typ: "" }, @@ -884,6 +933,22 @@ const typeMap: any = { { json: "fingerprintMaterial", js: "fingerprintMaterial", typ: "" }, { json: "publicKey", js: "publicKey", typ: "" }, ], false), + "FoldersCommand": o([ + { json: "create", js: "create", typ: u(undefined, r("FolderCreateRequest")) }, + { json: "list", js: "list", typ: u(undefined, m("any")) }, + { json: "update", js: "update", typ: u(undefined, r("FolderUpdateRequest")) }, + { json: "delete", js: "delete", typ: u(undefined, r("FolderDeleteRequest")) }, + ], false), + "FolderCreateRequest": o([ + { json: "name", js: "name", typ: "" }, + ], false), + "FolderDeleteRequest": o([ + { json: "id", js: "id", typ: "" }, + ], false), + "FolderUpdateRequest": o([ + { json: "id", js: "id", typ: "" }, + { json: "name", js: "name", typ: "" }, + ], false), "SecretVerificationRequest": o([ { json: "masterPassword", js: "masterPassword", typ: u(undefined, u(null, "")) }, { json: "otp", js: "otp", typ: u(undefined, u(null, "")) }, diff --git a/languages/python/BitwardenClient/schemas.py b/languages/python/BitwardenClient/schemas.py index 423521c00..880e1b761 100644 --- a/languages/python/BitwardenClient/schemas.py +++ b/languages/python/BitwardenClient/schemas.py @@ -1,6 +1,6 @@ from enum import Enum from dataclasses import dataclass -from typing import Optional, Any, List, TypeVar, Type, Callable, cast +from typing import Optional, Any, Dict, List, TypeVar, Type, Callable, cast from uuid import UUID @@ -32,9 +32,9 @@ def to_enum(c: Type[EnumT], x: Any) -> EnumT: return x.value -def from_list(f: Callable[[Any], T], x: Any) -> List[T]: - assert isinstance(x, list) - return [f(y) for y in x] +def from_dict(f: Callable[[Any], T], x: Any) -> Dict[str, T]: + assert isinstance(x, dict) + return { k: f(v) for (k, v) in x.items() } def to_class(c: Type[T], x: Any) -> dict: @@ -42,6 +42,11 @@ def to_class(c: Type[T], x: Any) -> dict: return cast(Any, x).to_dict() +def from_list(f: Callable[[Any], T], x: Any) -> List[T]: + assert isinstance(x, list) + return [f(y) for y in x] + + def from_bool(x: Any) -> bool: assert isinstance(x, bool) return x @@ -192,6 +197,104 @@ def to_dict(self) -> dict: return result +@dataclass +class FolderCreateRequest: + """Encrypted folder name""" + name: str + + @staticmethod + def from_dict(obj: Any) -> 'FolderCreateRequest': + assert isinstance(obj, dict) + name = from_str(obj.get("name")) + return FolderCreateRequest(name) + + def to_dict(self) -> dict: + result: dict = {} + result["name"] = from_str(self.name) + return result + + +@dataclass +class FolderDeleteRequest: + """ID of the folder to delete""" + id: UUID + + @staticmethod + def from_dict(obj: Any) -> 'FolderDeleteRequest': + assert isinstance(obj, dict) + id = UUID(obj.get("id")) + return FolderDeleteRequest(id) + + def to_dict(self) -> dict: + result: dict = {} + result["id"] = str(self.id) + return result + + +@dataclass +class FolderUpdateRequest: + """ID of the folder to update""" + id: UUID + """Encrypted folder name""" + name: str + + @staticmethod + def from_dict(obj: Any) -> 'FolderUpdateRequest': + assert isinstance(obj, dict) + id = UUID(obj.get("id")) + name = from_str(obj.get("name")) + return FolderUpdateRequest(id, name) + + def to_dict(self) -> dict: + result: dict = {} + result["id"] = str(self.id) + result["name"] = from_str(self.name) + return result + + +@dataclass +class FoldersCommand: + """> Requires Authentication > Requires an unlocked vault Creates a new folder with the + provided data + + > Requires Authentication > Requires an unlocked vault and calling Sync at least once + Lists all folders in the vault + + Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + + > Requires Authentication > Requires an unlocked vault Updates an existing folder with + the provided data given its ID + + > Requires Authentication > Requires an unlocked vault Deletes the folder associated with + the provided ID + """ + create: Optional[FolderCreateRequest] = None + list: Optional[Dict[str, Any]] = None + update: Optional[FolderUpdateRequest] = None + delete: Optional[FolderDeleteRequest] = None + + @staticmethod + def from_dict(obj: Any) -> 'FoldersCommand': + assert isinstance(obj, dict) + create = from_union([FolderCreateRequest.from_dict, from_none], obj.get("create")) + list = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("list")) + update = from_union([FolderUpdateRequest.from_dict, from_none], obj.get("update")) + delete = from_union([FolderDeleteRequest.from_dict, from_none], obj.get("delete")) + return FoldersCommand(create, list, update, delete) + + def to_dict(self) -> dict: + result: dict = {} + if self.create is not None: + result["create"] = from_union([lambda x: to_class(FolderCreateRequest, x), from_none], self.create) + if self.list is not None: + result["list"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.list) + if self.update is not None: + result["update"] = from_union([lambda x: to_class(FolderUpdateRequest, x), from_none], self.update) + if self.delete is not None: + result["delete"] = from_union([lambda x: to_class(FolderDeleteRequest, x), from_none], self.delete) + return result + + @dataclass class SecretVerificationRequest: """The user's master password to use for user verification. If supplied, this will be used @@ -655,6 +758,7 @@ class Command: sync: Optional[SyncRequest] = None secrets: Optional[SecretsCommand] = None projects: Optional[ProjectsCommand] = None + folders: Optional[FoldersCommand] = None @staticmethod def from_dict(obj: Any) -> 'Command': @@ -668,7 +772,8 @@ def from_dict(obj: Any) -> 'Command': sync = from_union([SyncRequest.from_dict, from_none], obj.get("sync")) secrets = from_union([SecretsCommand.from_dict, from_none], obj.get("secrets")) projects = from_union([ProjectsCommand.from_dict, from_none], obj.get("projects")) - return Command(password_login, api_key_login, access_token_login, session_login, get_user_api_key, fingerprint, sync, secrets, projects) + folders = from_union([FoldersCommand.from_dict, from_none], obj.get("folders")) + return Command(password_login, api_key_login, access_token_login, session_login, get_user_api_key, fingerprint, sync, secrets, projects, folders) def to_dict(self) -> dict: result: dict = {} @@ -690,6 +795,8 @@ def to_dict(self) -> dict: result["secrets"] = from_union([lambda x: to_class(SecretsCommand, x), from_none], self.secrets) if self.projects is not None: result["projects"] = from_union([lambda x: to_class(ProjectsCommand, x), from_none], self.projects) + if self.folders is not None: + result["folders"] = from_union([lambda x: to_class(FoldersCommand, x), from_none], self.folders) return result diff --git a/support/schemas/bitwarden_json/Command.json b/support/schemas/bitwarden_json/Command.json index 032a87727..5ab855e02 100644 --- a/support/schemas/bitwarden_json/Command.json +++ b/support/schemas/bitwarden_json/Command.json @@ -116,6 +116,18 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "folders" + ], + "properties": { + "folders": { + "$ref": "#/definitions/FoldersCommand" + } + }, + "additionalProperties": false } ], "definitions": { @@ -157,6 +169,10 @@ }, "additionalProperties": false }, + "EmptyRequest": { + "description": "An empty request that needs no parameters", + "type": "object" + }, "FingerprintRequest": { "type": "object", "required": [ @@ -175,6 +191,108 @@ }, "additionalProperties": false }, + "FolderCreateRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "Encrypted folder name", + "type": "string" + } + }, + "additionalProperties": false + }, + "FolderDeleteRequest": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "description": "ID of the folder to delete", + "type": "string", + "format": "uuid" + } + }, + "additionalProperties": false + }, + "FolderUpdateRequest": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "description": "ID of the folder to update", + "type": "string", + "format": "uuid" + }, + "name": { + "description": "Encrypted folder name", + "type": "string" + } + }, + "additionalProperties": false + }, + "FoldersCommand": { + "oneOf": [ + { + "description": "> Requires Authentication > Requires an unlocked vault Creates a new folder with the provided data", + "type": "object", + "required": [ + "create" + ], + "properties": { + "create": { + "$ref": "#/definitions/FolderCreateRequest" + } + }, + "additionalProperties": false + }, + { + "description": "> Requires Authentication > Requires an unlocked vault and calling Sync at least once Lists all folders in the vault\n\nReturns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse)", + "type": "object", + "required": [ + "list" + ], + "properties": { + "list": { + "$ref": "#/definitions/EmptyRequest" + } + }, + "additionalProperties": false + }, + { + "description": "> Requires Authentication > Requires an unlocked vault Updates an existing folder with the provided data given its ID", + "type": "object", + "required": [ + "update" + ], + "properties": { + "update": { + "$ref": "#/definitions/FolderUpdateRequest" + } + }, + "additionalProperties": false + }, + { + "description": "> Requires Authentication > Requires an unlocked vault Deletes the folder associated with the provided ID", + "type": "object", + "required": [ + "delete" + ], + "properties": { + "delete": { + "$ref": "#/definitions/FolderDeleteRequest" + } + }, + "additionalProperties": false + } + ] + }, "PasswordLoginRequest": { "description": "Login to Bitwarden with Username and Password", "type": "object", From 102dc89259b7f86355532ceee1cd91f3f90925d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Mon, 10 Jul 2023 19:04:50 +0200 Subject: [PATCH 6/9] Implement missing folder save and store_folders_from_sync --- crates/bitwarden/src/state/state.rs | 7 ++++++- crates/bitwarden/src/vault/folders/create.rs | 1 - crates/bitwarden/src/vault/folders/folder.rs | 11 ++++++++--- crates/bitwarden/src/vault/folders/folder_view.rs | 7 +------ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/bitwarden/src/state/state.rs b/crates/bitwarden/src/state/state.rs index f3b1bf8eb..693cfc2c4 100644 --- a/crates/bitwarden/src/state/state.rs +++ b/crates/bitwarden/src/state/state.rs @@ -38,7 +38,10 @@ impl State { ) -> Result<()> { // Before we create the storage profile, keep a copy of the current temporary storage (tokens, kdf params, etc) - use crate::client::{keys::store_keys_from_sync, profile::store_profile_from_sync}; + use crate::{ + client::{keys::store_keys_from_sync, profile::store_profile_from_sync}, + vault::folders::store_folders_from_sync, + }; let state = self.account.lock().await.get(); // Create the new account state, and load the temporary storage into it @@ -58,6 +61,8 @@ impl State { store_keys_from_sync(profile.as_ref(), self).await?; store_profile_from_sync(profile.as_ref(), self).await?; + store_folders_from_sync(data.folders.unwrap_or_default(), self).await?; + Ok(()) } diff --git a/crates/bitwarden/src/vault/folders/create.rs b/crates/bitwarden/src/vault/folders/create.rs index e18edceb4..9ba31d74a 100644 --- a/crates/bitwarden/src/vault/folders/create.rs +++ b/crates/bitwarden/src/vault/folders/create.rs @@ -1,4 +1,3 @@ -use bitwarden_api_api::models::FolderRequestModel; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/crates/bitwarden/src/vault/folders/folder.rs b/crates/bitwarden/src/vault/folders/folder.rs index 36c88da72..b08d4f8c4 100644 --- a/crates/bitwarden/src/vault/folders/folder.rs +++ b/crates/bitwarden/src/vault/folders/folder.rs @@ -143,10 +143,15 @@ async fn store_folder_response( response: FolderResponseModel, client: &Client, ) -> Result { - let folder = response.try_into()?; - - // TODO store folder on disk + let folder: Folder = response.try_into()?; + client + .get_state_service(FOLDERS_SERVICE) + .modify(|folders| { + folders.insert(folder.id, folder.clone()); + Ok(()) + }) + .await?; Ok(FolderFromDisk(folder)) } diff --git a/crates/bitwarden/src/vault/folders/folder_view.rs b/crates/bitwarden/src/vault/folders/folder_view.rs index d841ab7d9..115e1e306 100644 --- a/crates/bitwarden/src/vault/folders/folder_view.rs +++ b/crates/bitwarden/src/vault/folders/folder_view.rs @@ -4,12 +4,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; use super::folder::FolderFromDisk; -use crate::{ - client::encryption_settings::EncryptionSettings, - crypto::Decryptable, - error::{Error, Result}, - Client, -}; +use crate::{client::encryption_settings::EncryptionSettings, crypto::Decryptable, error::Result}; #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] From e139317dde506566440a4c9ea26e1cc648645a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Mon, 10 Jul 2023 19:10:08 +0200 Subject: [PATCH 7/9] Fix import --- crates/bitwarden-json/src/client.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-json/src/client.rs b/crates/bitwarden-json/src/client.rs index 6f66e41d5..e5ac18fbc 100644 --- a/crates/bitwarden-json/src/client.rs +++ b/crates/bitwarden-json/src/client.rs @@ -1,10 +1,13 @@ use bitwarden::client::client_settings::ClientSettings; use crate::{ - command::{Command, FoldersCommand, ProjectsCommand, SecretsCommand}, + command::{Command, ProjectsCommand, SecretsCommand}, response::ResponseIntoString, }; +#[cfg(feature = "internal")] +use crate::command::FoldersCommand; + pub struct Client(bitwarden::Client); impl Client { From 9577ceafaa9eed032758ed55876b81beaae4e300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Mon, 10 Jul 2023 19:31:25 +0200 Subject: [PATCH 8/9] Fix docs --- crates/bitwarden-json/src/command.rs | 2 +- crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts | 2 +- languages/csharp/schemas.cs | 2 +- languages/js_webassembly/bitwarden_client/schemas.ts | 2 +- languages/python/BitwardenClient/schemas.py | 2 +- support/schemas/bitwarden_json/Command.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bitwarden-json/src/command.rs b/crates/bitwarden-json/src/command.rs index 774bcb307..ac2a98587 100644 --- a/crates/bitwarden-json/src/command.rs +++ b/crates/bitwarden-json/src/command.rs @@ -187,7 +187,7 @@ pub enum FoldersCommand { /// > Requires an unlocked vault and calling Sync at least once /// Lists all folders in the vault /// - /// Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + /// Returns: [FoldersResponse](bitwarden::vault::folders::FoldersResponse) /// List(EmptyRequest), diff --git a/crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts b/crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts index dca1352ff..a68a60143 100644 --- a/crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts +++ b/crates/bitwarden-napi/src-ts/bitwarden_client/schemas.ts @@ -178,7 +178,7 @@ export interface FingerprintRequest { * > Requires Authentication > Requires an unlocked vault and calling Sync at least once * Lists all folders in the vault * - * Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + * Returns: [FoldersResponse](bitwarden::vault::folders::FoldersResponse) * * > Requires Authentication > Requires an unlocked vault Updates an existing folder with * the provided data given its ID diff --git a/languages/csharp/schemas.cs b/languages/csharp/schemas.cs index 2b869a7b1..ff67b09a9 100644 --- a/languages/csharp/schemas.cs +++ b/languages/csharp/schemas.cs @@ -200,7 +200,7 @@ public partial class FingerprintRequest /// > Requires Authentication > Requires an unlocked vault and calling Sync at least once /// Lists all folders in the vault /// - /// Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + /// Returns: [FoldersResponse](bitwarden::vault::folders::FoldersResponse) /// /// > Requires Authentication > Requires an unlocked vault Updates an existing folder with /// the provided data given its ID diff --git a/languages/js_webassembly/bitwarden_client/schemas.ts b/languages/js_webassembly/bitwarden_client/schemas.ts index dca1352ff..a68a60143 100644 --- a/languages/js_webassembly/bitwarden_client/schemas.ts +++ b/languages/js_webassembly/bitwarden_client/schemas.ts @@ -178,7 +178,7 @@ export interface FingerprintRequest { * > Requires Authentication > Requires an unlocked vault and calling Sync at least once * Lists all folders in the vault * - * Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + * Returns: [FoldersResponse](bitwarden::vault::folders::FoldersResponse) * * > Requires Authentication > Requires an unlocked vault Updates an existing folder with * the provided data given its ID diff --git a/languages/python/BitwardenClient/schemas.py b/languages/python/BitwardenClient/schemas.py index 880e1b761..c06062946 100644 --- a/languages/python/BitwardenClient/schemas.py +++ b/languages/python/BitwardenClient/schemas.py @@ -260,7 +260,7 @@ class FoldersCommand: > Requires Authentication > Requires an unlocked vault and calling Sync at least once Lists all folders in the vault - Returns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse) + Returns: [FoldersResponse](bitwarden::vault::folders::FoldersResponse) > Requires Authentication > Requires an unlocked vault Updates an existing folder with the provided data given its ID diff --git a/support/schemas/bitwarden_json/Command.json b/support/schemas/bitwarden_json/Command.json index 5ab855e02..fbe104981 100644 --- a/support/schemas/bitwarden_json/Command.json +++ b/support/schemas/bitwarden_json/Command.json @@ -253,7 +253,7 @@ "additionalProperties": false }, { - "description": "> Requires Authentication > Requires an unlocked vault and calling Sync at least once Lists all folders in the vault\n\nReturns: [FoldersResponse](bitwarden::platform::folders::FoldersResponse)", + "description": "> Requires Authentication > Requires an unlocked vault and calling Sync at least once Lists all folders in the vault\n\nReturns: [FoldersResponse](bitwarden::vault::folders::FoldersResponse)", "type": "object", "required": [ "list" From 818c78a544a62969fbb52c10e1680846d415151c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Mon, 17 Jul 2023 17:53:39 +0200 Subject: [PATCH 9/9] Fix warnings --- crates/bitwarden/src/vault/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/bitwarden/src/vault/mod.rs b/crates/bitwarden/src/vault/mod.rs index 7b7ab4f89..d6dfc8dd4 100644 --- a/crates/bitwarden/src/vault/mod.rs +++ b/crates/bitwarden/src/vault/mod.rs @@ -1,2 +1,4 @@ +#[cfg(feature = "internal")] mod client_folders; +#[cfg(feature = "internal")] pub mod folders;