1
0
mirror of https://github.com/dani-garcia/vaultwarden.git synced 2024-11-10 13:02:41 +01:00

Delete associated favorites when deleting a cipher or user

This prevents foreign key constraint violations.
This commit is contained in:
Jeremy Lin 2020-08-26 01:27:38 -07:00
parent 4c324e1160
commit 175d647e47
4 changed files with 108 additions and 52 deletions

View File

@ -2,7 +2,15 @@ use chrono::{NaiveDateTime, Utc};
use serde_json::Value; use serde_json::Value;
use super::{ use super::{
Attachment, CollectionCipher, FolderCipher, Organization, User, UserOrgStatus, UserOrgType, UserOrganization, Attachment,
CollectionCipher,
Favorite,
FolderCipher,
Organization,
User,
UserOrgStatus,
UserOrgType,
UserOrganization,
}; };
db_object! { db_object! {
@ -213,6 +221,7 @@ impl Cipher {
FolderCipher::delete_all_by_cipher(&self.uuid, conn)?; FolderCipher::delete_all_by_cipher(&self.uuid, conn)?;
CollectionCipher::delete_all_by_cipher(&self.uuid, conn)?; CollectionCipher::delete_all_by_cipher(&self.uuid, conn)?;
Attachment::delete_all_by_cipher(&self.uuid, conn)?; Attachment::delete_all_by_cipher(&self.uuid, conn)?;
Favorite::delete_all_by_cipher(&self.uuid, conn)?;
db_run! { conn: { db_run! { conn: {
diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid))) diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid)))
@ -340,51 +349,14 @@ impl Cipher {
// Returns whether this cipher is a favorite of the specified user. // Returns whether this cipher is a favorite of the specified user.
pub fn is_favorite(&self, user_uuid: &str, conn: &DbConn) -> bool { pub fn is_favorite(&self, user_uuid: &str, conn: &DbConn) -> bool {
db_run!{ conn: { Favorite::is_favorite(&self.uuid, user_uuid, conn)
let query = favorites::table
.filter(favorites::user_uuid.eq(user_uuid))
.filter(favorites::cipher_uuid.eq(&self.uuid))
.count();
query.first::<i64>(conn).ok().unwrap_or(0) != 0
}}
} }
// Updates whether this cipher is a favorite of the specified user. // Sets whether this cipher is a favorite of the specified user.
pub fn set_favorite(&self, favorite: Option<bool>, user_uuid: &str, conn: &DbConn) -> EmptyResult { pub fn set_favorite(&self, favorite: Option<bool>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
if favorite.is_none() { match favorite {
// No change requested. None => Ok(()), // No change requested.
return Ok(()); Some(status) => Favorite::set_favorite(status, &self.uuid, user_uuid, conn),
}
let (old, new) = (self.is_favorite(user_uuid, &conn), favorite.unwrap());
match (old, new) {
(false, true) => {
User::update_uuid_revision(user_uuid, &conn);
db_run!{ conn: {
diesel::insert_into(favorites::table)
.values((
favorites::user_uuid.eq(user_uuid),
favorites::cipher_uuid.eq(&self.uuid),
))
.execute(conn)
.map_res("Error adding favorite")
}}
}
(true, false) => {
User::update_uuid_revision(user_uuid, &conn);
db_run!{ conn: {
diesel::delete(
favorites::table
.filter(favorites::user_uuid.eq(user_uuid))
.filter(favorites::cipher_uuid.eq(&self.uuid))
)
.execute(conn)
.map_res("Error removing favorite")
}}
}
// Otherwise, the favorite status is already what it should be.
_ => Ok(())
} }
} }

83
src/db/models/favorite.rs Normal file
View File

@ -0,0 +1,83 @@
use super::{Cipher, User};
db_object! {
#[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
#[table_name = "favorites"]
#[belongs_to(User, foreign_key = "user_uuid")]
#[belongs_to(Cipher, foreign_key = "cipher_uuid")]
#[primary_key(user_uuid, cipher_uuid)]
pub struct Favorite {
pub user_uuid: String,
pub cipher_uuid: String,
}
}
use crate::db::DbConn;
use crate::api::EmptyResult;
use crate::error::MapResult;
impl Favorite {
// Returns whether the specified cipher is a favorite of the specified user.
pub fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> bool {
db_run!{ conn: {
let query = favorites::table
.filter(favorites::cipher_uuid.eq(cipher_uuid))
.filter(favorites::user_uuid.eq(user_uuid))
.count();
query.first::<i64>(conn).ok().unwrap_or(0) != 0
}}
}
// Sets whether the specified cipher is a favorite of the specified user.
pub fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> EmptyResult {
let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, &conn), favorite);
match (old, new) {
(false, true) => {
User::update_uuid_revision(user_uuid, &conn);
db_run!{ conn: {
diesel::insert_into(favorites::table)
.values((
favorites::user_uuid.eq(user_uuid),
favorites::cipher_uuid.eq(cipher_uuid),
))
.execute(conn)
.map_res("Error adding favorite")
}}
}
(true, false) => {
User::update_uuid_revision(user_uuid, &conn);
db_run!{ conn: {
diesel::delete(
favorites::table
.filter(favorites::user_uuid.eq(user_uuid))
.filter(favorites::cipher_uuid.eq(cipher_uuid))
)
.execute(conn)
.map_res("Error removing favorite")
}}
}
// Otherwise, the favorite status is already what it should be.
_ => Ok(())
}
}
// Delete all favorite entries associated with the specified cipher.
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid)))
.execute(conn)
.map_res("Error removing favorites by cipher")
}}
}
// Delete all favorite entries associated with the specified user.
pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid)))
.execute(conn)
.map_res("Error removing favorites by user")
}}
}
}

View File

@ -1,21 +1,21 @@
mod attachment; mod attachment;
mod cipher; mod cipher;
mod device;
mod folder;
mod user;
mod collection; mod collection;
mod device;
mod favorite;
mod folder;
mod org_policy;
mod organization; mod organization;
mod two_factor; mod two_factor;
mod org_policy; mod user;
pub use self::attachment::Attachment; pub use self::attachment::Attachment;
pub use self::cipher::Cipher; pub use self::cipher::Cipher;
pub use self::collection::{Collection, CollectionCipher, CollectionUser}; pub use self::collection::{Collection, CollectionCipher, CollectionUser};
pub use self::device::Device; pub use self::device::Device;
pub use self::favorite::Favorite;
pub use self::folder::{Folder, FolderCipher}; pub use self::folder::{Folder, FolderCipher};
pub use self::organization::Organization; pub use self::org_policy::{OrgPolicy, OrgPolicyType};
pub use self::organization::{UserOrgStatus, UserOrgType, UserOrganization}; pub use self::organization::{Organization, UserOrgStatus, UserOrgType, UserOrganization};
pub use self::two_factor::{TwoFactor, TwoFactorType}; pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::user::{Invitation, User}; pub use self::user::{Invitation, User};
pub use self::org_policy::{OrgPolicy, OrgPolicyType};

View File

@ -128,7 +128,7 @@ impl User {
} }
} }
use super::{Cipher, Device, Folder, TwoFactor, UserOrgType, UserOrganization}; use super::{Cipher, Device, Favorite, Folder, TwoFactor, UserOrgType, UserOrganization};
use crate::db::DbConn; use crate::db::DbConn;
use crate::api::EmptyResult; use crate::api::EmptyResult;
@ -205,6 +205,7 @@ impl User {
UserOrganization::delete_all_by_user(&self.uuid, conn)?; UserOrganization::delete_all_by_user(&self.uuid, conn)?;
Cipher::delete_all_by_user(&self.uuid, conn)?; Cipher::delete_all_by_user(&self.uuid, conn)?;
Favorite::delete_all_by_user(&self.uuid, conn)?;
Folder::delete_all_by_user(&self.uuid, conn)?; Folder::delete_all_by_user(&self.uuid, conn)?;
Device::delete_all_by_user(&self.uuid, conn)?; Device::delete_all_by_user(&self.uuid, conn)?;
TwoFactor::delete_all_by_user(&self.uuid, conn)?; TwoFactor::delete_all_by_user(&self.uuid, conn)?;