mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-31 02:08:20 +02:00 
			
		
		
		
	Fix several more multi select push issues (#6151)
* Fix several more multi select push issues There were some more items which would still overload the push endpoint. This PR fixes the remaining items (I hope). I also encountered a missing endpoint for restoring multiple ciphers from the trash via the admin console. Overall, we could improve a lot of these items in a different way. Like bundle all SQL Queries etc... But that takes more time, and this fixes overloading the Bitwarden push servers, and speeds up these specific actions. Signed-off-by: BlackDex <black.dex@gmail.com> * Update src/api/core/ciphers.rs Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com> --------- Signed-off-by: BlackDex <black.dex@gmail.com> Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							8fd0ee4211
						
					
				
				
					commit
					2a5489a4b2
				
			| @@ -78,6 +78,7 @@ pub fn routes() -> Vec<Route> { | ||||
|         restore_cipher_put, | ||||
|         restore_cipher_put_admin, | ||||
|         restore_cipher_selected, | ||||
|         restore_cipher_selected_admin, | ||||
|         delete_all, | ||||
|         move_cipher_selected, | ||||
|         move_cipher_selected_put, | ||||
| @@ -318,7 +319,7 @@ async fn post_ciphers_create( | ||||
|     // or otherwise), we can just ignore this field entirely. | ||||
|     data.cipher.last_known_revision_date = None; | ||||
|  | ||||
|     share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &nt).await | ||||
|     share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &nt, None).await | ||||
| } | ||||
|  | ||||
| /// Called when creating a new user-owned cipher. | ||||
| @@ -920,7 +921,7 @@ async fn post_cipher_share( | ||||
| ) -> JsonResult { | ||||
|     let data: ShareCipherData = data.into_inner(); | ||||
|  | ||||
|     share_cipher_by_uuid(&cipher_id, data, &headers, &mut conn, &nt).await | ||||
|     share_cipher_by_uuid(&cipher_id, data, &headers, &mut conn, &nt, None).await | ||||
| } | ||||
|  | ||||
| #[put("/ciphers/<cipher_id>/share", data = "<data>")] | ||||
| @@ -933,7 +934,7 @@ async fn put_cipher_share( | ||||
| ) -> JsonResult { | ||||
|     let data: ShareCipherData = data.into_inner(); | ||||
|  | ||||
|     share_cipher_by_uuid(&cipher_id, data, &headers, &mut conn, &nt).await | ||||
|     share_cipher_by_uuid(&cipher_id, data, &headers, &mut conn, &nt, None).await | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize)] | ||||
| @@ -973,11 +974,16 @@ async fn put_cipher_share_selected( | ||||
|         }; | ||||
|  | ||||
|         match shared_cipher_data.cipher.id.take() { | ||||
|             Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &nt).await?, | ||||
|             Some(id) => { | ||||
|                 share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &nt, Some(UpdateType::None)).await? | ||||
|             } | ||||
|             None => err!("Request missing ids field"), | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     // Multi share actions do not send out a push for each cipher, we need to send a general sync here | ||||
|     nt.send_user_update(UpdateType::SyncCiphers, &headers.user, &headers.device.push_uuid, &mut conn).await; | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @@ -987,6 +993,7 @@ async fn share_cipher_by_uuid( | ||||
|     headers: &Headers, | ||||
|     conn: &mut DbConn, | ||||
|     nt: &Notify<'_>, | ||||
|     override_ut: Option<UpdateType>, | ||||
| ) -> JsonResult { | ||||
|     let mut cipher = match Cipher::find_by_uuid(cipher_id, conn).await { | ||||
|         Some(cipher) => { | ||||
| @@ -1018,7 +1025,10 @@ async fn share_cipher_by_uuid( | ||||
|     }; | ||||
|  | ||||
|     // When LastKnownRevisionDate is None, it is a new cipher, so send CipherCreate. | ||||
|     let ut = if data.cipher.last_known_revision_date.is_some() { | ||||
|     // If there is an override, like when handling multiple items, we want to prevent a push notification for every single item | ||||
|     let ut = if let Some(ut) = override_ut { | ||||
|         ut | ||||
|     } else if data.cipher.last_known_revision_date.is_some() { | ||||
|         UpdateType::SyncCipherUpdate | ||||
|     } else { | ||||
|         UpdateType::SyncCipherCreate | ||||
| @@ -1517,7 +1527,7 @@ async fn delete_cipher_selected_put_admin( | ||||
|  | ||||
| #[put("/ciphers/<cipher_id>/restore")] | ||||
| async fn restore_cipher_put(cipher_id: CipherId, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { | ||||
|     _restore_cipher_by_uuid(&cipher_id, &headers, &mut conn, &nt).await | ||||
|     _restore_cipher_by_uuid(&cipher_id, &headers, false, &mut conn, &nt).await | ||||
| } | ||||
|  | ||||
| #[put("/ciphers/<cipher_id>/restore-admin")] | ||||
| @@ -1527,7 +1537,17 @@ async fn restore_cipher_put_admin( | ||||
|     mut conn: DbConn, | ||||
|     nt: Notify<'_>, | ||||
| ) -> JsonResult { | ||||
|     _restore_cipher_by_uuid(&cipher_id, &headers, &mut conn, &nt).await | ||||
|     _restore_cipher_by_uuid(&cipher_id, &headers, false, &mut conn, &nt).await | ||||
| } | ||||
|  | ||||
| #[put("/ciphers/restore-admin", data = "<data>")] | ||||
| async fn restore_cipher_selected_admin( | ||||
|     data: Json<CipherIdsData>, | ||||
|     headers: Headers, | ||||
|     mut conn: DbConn, | ||||
|     nt: Notify<'_>, | ||||
| ) -> JsonResult { | ||||
|     _restore_multiple_ciphers(data, &headers, &mut conn, &nt).await | ||||
| } | ||||
|  | ||||
| #[put("/ciphers/restore", data = "<data>")] | ||||
| @@ -1555,35 +1575,47 @@ async fn move_cipher_selected( | ||||
|     nt: Notify<'_>, | ||||
| ) -> EmptyResult { | ||||
|     let data = data.into_inner(); | ||||
|     let user_id = headers.user.uuid; | ||||
|     let user_id = &headers.user.uuid; | ||||
|  | ||||
|     if let Some(ref folder_id) = data.folder_id { | ||||
|         if Folder::find_by_uuid_and_user(folder_id, &user_id, &mut conn).await.is_none() { | ||||
|         if Folder::find_by_uuid_and_user(folder_id, user_id, &mut conn).await.is_none() { | ||||
|             err!("Invalid folder", "Folder does not exist or belongs to another user"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for cipher_id in data.ids { | ||||
|         let Some(cipher) = Cipher::find_by_uuid(&cipher_id, &mut conn).await else { | ||||
|             err!("Cipher doesn't exist") | ||||
|         }; | ||||
|     let cipher_count = data.ids.len(); | ||||
|     let mut single_cipher: Option<Cipher> = None; | ||||
|  | ||||
|         if !cipher.is_accessible_to_user(&user_id, &mut conn).await { | ||||
|             err!("Cipher is not accessible by user") | ||||
|     // TODO: Convert this to use a single query (or at least less) to update all items | ||||
|     // Find all ciphers a user has access to, all others will be ignored | ||||
|     let accessible_ciphers = Cipher::find_by_user_and_ciphers(user_id, &data.ids, &mut conn).await; | ||||
|     let accessible_ciphers_count = accessible_ciphers.len(); | ||||
|     for cipher in accessible_ciphers { | ||||
|         cipher.move_to_folder(data.folder_id.clone(), user_id, &mut conn).await?; | ||||
|         if cipher_count == 1 { | ||||
|             single_cipher = Some(cipher); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         // Move cipher | ||||
|         cipher.move_to_folder(data.folder_id.clone(), &user_id, &mut conn).await?; | ||||
|  | ||||
|     if let Some(cipher) = single_cipher { | ||||
|         nt.send_cipher_update( | ||||
|             UpdateType::SyncCipherUpdate, | ||||
|             &cipher, | ||||
|             std::slice::from_ref(&user_id), | ||||
|             std::slice::from_ref(user_id), | ||||
|             &headers.device, | ||||
|             None, | ||||
|             &mut conn, | ||||
|         ) | ||||
|         .await; | ||||
|     } else { | ||||
|         // Multi move actions do not send out a push for each cipher, we need to send a general sync here | ||||
|         nt.send_user_update(UpdateType::SyncCiphers, &headers.user, &headers.device.push_uuid, &mut conn).await; | ||||
|     } | ||||
|  | ||||
|     if cipher_count != accessible_ciphers_count { | ||||
|         err!(format!( | ||||
|             "Not all ciphers are moved! {accessible_ciphers_count} of the selected {cipher_count} were moved." | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| @@ -1764,6 +1796,7 @@ async fn _delete_multiple_ciphers( | ||||
| async fn _restore_cipher_by_uuid( | ||||
|     cipher_id: &CipherId, | ||||
|     headers: &Headers, | ||||
|     multi_restore: bool, | ||||
|     conn: &mut DbConn, | ||||
|     nt: &Notify<'_>, | ||||
| ) -> JsonResult { | ||||
| @@ -1778,15 +1811,17 @@ async fn _restore_cipher_by_uuid( | ||||
|     cipher.deleted_at = None; | ||||
|     cipher.save(conn).await?; | ||||
|  | ||||
|     nt.send_cipher_update( | ||||
|         UpdateType::SyncCipherUpdate, | ||||
|         &cipher, | ||||
|         &cipher.update_users_revision(conn).await, | ||||
|         &headers.device, | ||||
|         None, | ||||
|         conn, | ||||
|     ) | ||||
|     .await; | ||||
|     if !multi_restore { | ||||
|         nt.send_cipher_update( | ||||
|             UpdateType::SyncCipherUpdate, | ||||
|             &cipher, | ||||
|             &cipher.update_users_revision(conn).await, | ||||
|             &headers.device, | ||||
|             None, | ||||
|             conn, | ||||
|         ) | ||||
|         .await; | ||||
|     } | ||||
|  | ||||
|     if let Some(org_id) = &cipher.organization_uuid { | ||||
|         log_event( | ||||
| @@ -1814,12 +1849,15 @@ async fn _restore_multiple_ciphers( | ||||
|  | ||||
|     let mut ciphers: Vec<Value> = Vec::new(); | ||||
|     for cipher_id in data.ids { | ||||
|         match _restore_cipher_by_uuid(&cipher_id, headers, conn, nt).await { | ||||
|         match _restore_cipher_by_uuid(&cipher_id, headers, true, conn, nt).await { | ||||
|             Ok(json) => ciphers.push(json.into_inner()), | ||||
|             err => return err, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Multi move actions do not send out a push for each cipher, we need to send a general sync here | ||||
|     nt.send_user_update(UpdateType::SyncCiphers, &headers.user, &headers.device.push_uuid, conn).await; | ||||
|  | ||||
|     Ok(Json(json!({ | ||||
|       "data": ciphers, | ||||
|       "object": "list", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user