diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 8aaefb7c..bcf8c797 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -306,7 +306,9 @@ pub fn update_cipher_from_data( cipher.save(&conn)?; cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn)?; - nt.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn)); + if ut != UpdateType::None { + nt.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn)); + } Ok(()) } @@ -351,25 +353,18 @@ fn post_ciphers_import(data: JsonUpcase, headers: Headers, conn: DbC } // Read and create the ciphers - for (index, cipher_data) in data.Ciphers.into_iter().enumerate() { + for (index, mut cipher_data) in data.Ciphers.into_iter().enumerate() { let folder_uuid = relations_map.get(&index).map(|i| folders[*i].uuid.clone()); + cipher_data.FolderId = folder_uuid; let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); - update_cipher_from_data( - &mut cipher, - cipher_data, - &headers, - false, - &conn, - &nt, - UpdateType::CipherCreate, - )?; - - cipher.move_to_folder(folder_uuid, &headers.user.uuid.clone(), &conn)?; + update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None)?; } let mut user = headers.user; - user.update_revision(&conn) + user.update_revision(&conn)?; + nt.send_user_update(UpdateType::Vault, &user); + Ok(()) } #[put("/ciphers//admin", data = "")] @@ -637,7 +632,14 @@ fn share_cipher_by_uuid( } #[post("/ciphers//attachment", format = "multipart/form-data", data = "")] -fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { +fn post_attachment( + uuid: String, + data: Data, + content_type: &ContentType, + headers: Headers, + conn: DbConn, + nt: Notify, +) -> JsonResult { let cipher = match Cipher::find_by_uuid(&uuid, &conn) { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), @@ -816,56 +818,60 @@ fn delete_cipher_selected_post(data: JsonUpcase, headers: Headers, conn: delete_cipher_selected(data, headers, conn, nt) } +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct MoveCipherData { + FolderId: Option, + Ids: Vec, +} + #[post("/ciphers/move", data = "")] -fn move_cipher_selected(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult { +fn move_cipher_selected(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult { let data = data.into_inner().data; + let user_uuid = headers.user.uuid; - let folder_id = match data.get("FolderId") { - Some(folder_id) => match folder_id.as_str() { - Some(folder_id) => match Folder::find_by_uuid(folder_id, &conn) { - Some(folder) => { - if folder.user_uuid != headers.user.uuid { - err!("Folder is not owned by user") - } - Some(folder.uuid) + if let Some(ref folder_id) = data.FolderId { + match Folder::find_by_uuid(folder_id, &conn) { + Some(folder) => { + if folder.user_uuid != user_uuid { + err!("Folder is not owned by user") } - None => err!("Folder doesn't exist"), - }, - None => err!("Folder id provided in wrong format"), - }, - None => None, - }; + } + None => err!("Folder doesn't exist"), + } + } - let uuids = match data.get("Ids") { - Some(ids) => match ids.as_array() { - Some(ids) => ids.iter().filter_map(Value::as_str), - None => err!("Posted ids field is not an array"), - }, - None => err!("Request missing ids field"), - }; - - for uuid in uuids { - let mut cipher = match Cipher::find_by_uuid(uuid, &conn) { + for uuid in data.Ids { + let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) { + if !cipher.is_accessible_to_user(&user_uuid, &conn) { err!("Cipher is not accessible by user") } // Move cipher - cipher.move_to_folder(folder_id.clone(), &headers.user.uuid, &conn)?; + cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &conn)?; cipher.save(&conn)?; - nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn)); + nt.send_cipher_update( + UpdateType::CipherUpdate, + &cipher, + &User::update_uuid_revision(&user_uuid, &conn), + ); } Ok(()) } #[put("/ciphers/move", data = "")] -fn move_cipher_selected_put(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult { +fn move_cipher_selected_put( + data: JsonUpcase, + headers: Headers, + conn: DbConn, + nt: Notify, +) -> EmptyResult { move_cipher_selected(data, headers, conn, nt) } @@ -874,7 +880,7 @@ fn delete_all(data: JsonUpcase, headers: Headers, conn: DbConn, nt let data: PasswordData = data.into_inner().data; let password_hash = data.MasterPasswordHash; - let user = headers.user; + let mut user = headers.user; if !user.check_valid_password(&password_hash) { err!("Invalid password") @@ -883,15 +889,15 @@ fn delete_all(data: JsonUpcase, headers: Headers, conn: DbConn, nt // Delete ciphers and their attachments for cipher in Cipher::find_owned_by_user(&user.uuid, &conn) { cipher.delete(&conn)?; - nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(&conn)); } // Delete folders for f in Folder::find_by_user(&user.uuid, &conn) { f.delete(&conn)?; - nt.send_folder_update(UpdateType::FolderDelete, &f); } + user.update_revision(&conn)?; + nt.send_user_update(UpdateType::Vault, &user); Ok(()) } diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 4f6aae6e..d5f86064 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -240,7 +240,6 @@ impl WebSocketUsers { } // NOTE: The last modified date needs to be updated before calling these methods - #[allow(dead_code)] pub fn send_user_update(&self, ut: UpdateType, user: &User) { let data = create_update( vec![ @@ -325,6 +324,7 @@ fn create_ping() -> Vec { } #[allow(dead_code)] +#[derive(PartialEq)] pub enum UpdateType { CipherUpdate = 0, CipherCreate = 1, @@ -340,6 +340,8 @@ pub enum UpdateType { SyncSettings = 10, LogOut = 11, + + None = 100, } use rocket::State; diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index 3a7c9dff..47d2784a 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -196,40 +196,28 @@ impl Cipher { } pub fn move_to_folder(&self, folder_uuid: Option, user_uuid: &str, conn: &DbConn) -> EmptyResult { - match self.get_folder_uuid(&user_uuid, &conn) { - None => { - match folder_uuid { - Some(new_folder) => { - self.update_users_revision(conn); - let folder_cipher = FolderCipher::new(&new_folder, &self.uuid); - folder_cipher.save(&conn) - } - None => Ok(()), //nothing to do - } - } - Some(current_folder) => { - match folder_uuid { - Some(new_folder) => { - if current_folder == new_folder { - Ok(()) //nothing to do - } else { - self.update_users_revision(conn); - if let Some(current_folder) = - FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) - { - current_folder.delete(&conn)?; - } - FolderCipher::new(&new_folder, &self.uuid).save(&conn) - } - } - None => { - self.update_users_revision(conn); - match FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) { - Some(current_folder) => current_folder.delete(&conn), - None => err!("Couldn't move from previous folder"), - } - } + User::update_uuid_revision(user_uuid, &conn); + + match (self.get_folder_uuid(&user_uuid, &conn), folder_uuid) { + // No changes + (None, None) => Ok(()), + (Some(ref old), Some(ref new)) if old == new => Ok(()), + + // Add to folder + (None, Some(new)) => FolderCipher::new(&new, &self.uuid).save(&conn), + + // Remove from folder + (Some(old), None) => match FolderCipher::find_by_folder_and_cipher(&old, &self.uuid, &conn) { + Some(old) => old.delete(&conn), + None => err!("Couldn't move from previous folder"), + }, + + // Move to another folder + (Some(old), Some(new)) => { + if let Some(old) = FolderCipher::find_by_folder_and_cipher(&old, &self.uuid, &conn) { + old.delete(&conn)?; } + FolderCipher::new(&new, &self.uuid).save(&conn) } } } diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index 3738bd9c..42d78314 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -241,7 +241,9 @@ impl CollectionUser { pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { CollectionUser::find_by_collection(&collection_uuid, conn) .iter() - .for_each(|collection| User::update_uuid_revision(&collection.user_uuid, conn)); + .for_each(|collection| { + User::update_uuid_revision(&collection.user_uuid, conn); + }); diesel::delete(users_collections::table.filter(users_collections::collection_uuid.eq(collection_uuid))) .execute(&**conn) diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 429fc4ea..127fa78a 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -172,12 +172,14 @@ impl User { .map_res("Error deleting user") } - pub fn update_uuid_revision(uuid: &str, conn: &DbConn) { + pub fn update_uuid_revision(uuid: &str, conn: &DbConn) -> Vec { if let Some(mut user) = User::find_by_uuid(&uuid, conn) { if user.update_revision(conn).is_err() { warn!("Failed to update revision for {}", user.email); }; }; + + vec![uuid.to_string()] } pub fn update_revision(&mut self, conn: &DbConn) -> EmptyResult {