vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

commit b71d9dd53e447eb66786d0890fb4b595210de158
parent 887e320e7f8dfc62d9b3ed08aca216cd7ad229f1
Author: BlackDex <black.dex@gmail.com>
Date:   Tue, 21 Jun 2022 17:36:07 +0200

Fix for issue #2566

This PR fixes #2566
If Organizational syncs returned a FolderId it would cause the web-vault
to hide the cipher because there is a FolderId set. Upstream seems to
not return FolderId and Favorite. When set to null/false it will behave
the same.

In this PR I have added a new CipherSyncType enum to select which type
of sync to execute, and return an empty list for both Folders and Favorites if this is for Orgs.
This also reduces the database load a bit since it will not execute those queries.

Diffstat:
Msrc/api/core/ciphers.rs | 36+++++++++++++++++++++++++-----------
Msrc/api/core/emergency_access.rs | 8++++++--
Msrc/api/core/mod.rs | 2+-
Msrc/api/core/organizations.rs | 6+++---
4 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs @@ -104,7 +104,7 @@ async fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json<Value> { // Get all ciphers which are visible by the user let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, &conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::User, &conn).await; // Lets generate the ciphers_json using all the gathered info let ciphers_json: Vec<Value> = stream::iter(ciphers) @@ -154,7 +154,7 @@ async fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json<Value> { #[get("/ciphers")] async fn get_ciphers(headers: Headers, conn: DbConn) -> Json<Value> { let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, &conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::User, &conn).await; let ciphers_json = stream::iter(ciphers) .then(|c| async { @@ -1486,25 +1486,39 @@ pub struct CipherSyncData { pub user_collections: HashMap<String, CollectionUser>, } +pub enum CipherSyncType { + User, + Organization, +} + impl CipherSyncData { - pub async fn new(user_uuid: &str, ciphers: &Vec<Cipher>, conn: &DbConn) -> Self { + pub async fn new(user_uuid: &str, ciphers: &Vec<Cipher>, sync_type: CipherSyncType, conn: &DbConn) -> Self { // Generate a list of Cipher UUID's to be used during a query filter with an eq_any. let cipher_uuids = stream::iter(ciphers).map(|c| c.uuid.to_string()).collect::<Vec<String>>().await; + let mut cipher_folders: HashMap<String, String> = HashMap::new(); + let mut cipher_favorites: HashSet<String> = HashSet::new(); + match sync_type { + // User Sync supports Folders and Favorits + CipherSyncType::User => { + // Generate a HashMap with the Cipher UUID as key and the Folder UUID as value + cipher_folders = stream::iter(FolderCipher::find_by_user(user_uuid, conn).await).collect().await; + + // Generate a HashSet of all the Cipher UUID's which are marked as favorite + cipher_favorites = + stream::iter(Favorite::get_all_cipher_uuid_by_user(user_uuid, conn).await).collect().await; + } + // Organization Sync does not support Folders and Favorits. + // If these are set, it will cause issues in the web-vault. + CipherSyncType::Organization => {} + } + // Generate a list of Cipher UUID's containing a Vec with one or more Attachment records let mut cipher_attachments: HashMap<String, Vec<Attachment>> = HashMap::new(); for attachment in Attachment::find_all_by_ciphers(&cipher_uuids, conn).await { cipher_attachments.entry(attachment.cipher_uuid.to_string()).or_default().push(attachment); } - // Generate a HashMap with the Cipher UUID as key and the Folder UUID as value - let cipher_folders: HashMap<String, String> = - stream::iter(FolderCipher::find_by_user(user_uuid, conn).await).collect().await; - - // Generate a HashSet of all the Cipher UUID's which are marked as favorite - let cipher_favorites: HashSet<String> = - stream::iter(Favorite::get_all_cipher_uuid_by_user(user_uuid, conn).await).collect().await; - // Generate a HashMap with the Cipher UUID as key and one or more Collection UUID's let mut cipher_collections: HashMap<String, Vec<String>> = HashMap::new(); for (cipher, collection) in Cipher::get_collections_with_cipher_by_user(user_uuid, conn).await { diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs @@ -5,7 +5,10 @@ use serde_json::Value; use std::borrow::Borrow; use crate::{ - api::{core::CipherSyncData, EmptyResult, JsonResult, JsonUpcase, NumberOrString}, + api::{ + core::{CipherSyncData, CipherSyncType}, + EmptyResult, JsonResult, JsonUpcase, NumberOrString, + }, auth::{decode_emergency_access_invite, Headers}, db::{models::*, DbConn, DbPool}, mail, CONFIG, @@ -596,7 +599,8 @@ async fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) } let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &conn).await; - let cipher_sync_data = CipherSyncData::new(&emergency_access.grantor_uuid, &ciphers, &conn).await; + let cipher_sync_data = + CipherSyncData::new(&emergency_access.grantor_uuid, &ciphers, CipherSyncType::User, &conn).await; let ciphers_json = stream::iter(ciphers) .then(|c| async { diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs @@ -7,7 +7,7 @@ mod sends; pub mod two_factor; pub use ciphers::purge_trashed_ciphers; -pub use ciphers::CipherSyncData; +pub use ciphers::{CipherSyncData, CipherSyncType}; pub use emergency_access::{emergency_notification_reminder_job, emergency_request_timeout_job}; pub use sends::purge_sends; pub use two_factor::send_incomplete_2fa_notifications; diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs @@ -5,8 +5,8 @@ use serde_json::Value; use crate::{ api::{ - core::CipherSyncData, EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, Notify, NumberOrString, PasswordData, - UpdateType, + core::{CipherSyncData, CipherSyncType}, + EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, Notify, NumberOrString, PasswordData, UpdateType, }, auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, db::{models::*, DbConn}, @@ -487,7 +487,7 @@ struct OrgIdData { #[get("/ciphers/organization-details?<data..>")] async fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> Json<Value> { let ciphers = Cipher::find_by_org(&data.organization_id, &conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, &conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::Organization, &conn).await; let ciphers_json = stream::iter(ciphers) .then(|c| async {