mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-11-04 04:08:20 +02:00 
			
		
		
		
	Merge pull request #1106 from jjlin/favorites
Track favorites on a per-user basis
This commit is contained in:
		@@ -303,7 +303,6 @@ pub fn update_cipher_from_data(
 | 
			
		||||
    type_data["PasswordHistory"] = data.PasswordHistory.clone().unwrap_or(Value::Null);
 | 
			
		||||
    // TODO: ******* Backwards compat end **********
 | 
			
		||||
 | 
			
		||||
    cipher.favorite = data.Favorite.unwrap_or(false);
 | 
			
		||||
    cipher.name = data.Name;
 | 
			
		||||
    cipher.notes = data.Notes;
 | 
			
		||||
    cipher.fields = data.Fields.map(|f| f.to_string());
 | 
			
		||||
@@ -312,6 +311,7 @@ pub fn update_cipher_from_data(
 | 
			
		||||
 | 
			
		||||
    cipher.save(&conn)?;
 | 
			
		||||
    cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn)?;
 | 
			
		||||
    cipher.set_favorite(data.Favorite, &headers.user.uuid, &conn)?;
 | 
			
		||||
 | 
			
		||||
    if ut != UpdateType::None {
 | 
			
		||||
        nt.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn));
 | 
			
		||||
@@ -410,6 +410,11 @@ fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn
 | 
			
		||||
        None => err!("Cipher doesn't exist"),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // TODO: Check if only the folder ID or favorite status is being changed.
 | 
			
		||||
    // These are per-user properties that technically aren't part of the
 | 
			
		||||
    // cipher itself, so the user shouldn't need write access to change these.
 | 
			
		||||
    // Interestingly, upstream Bitwarden doesn't properly handle this either.
 | 
			
		||||
 | 
			
		||||
    if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
 | 
			
		||||
        err!("Cipher is not write accessible")
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ pub struct Cipher {
 | 
			
		||||
 | 
			
		||||
    pub data: String,
 | 
			
		||||
 | 
			
		||||
    pub favorite: bool,
 | 
			
		||||
    pub password_history: Option<String>,
 | 
			
		||||
    pub deleted_at: Option<NaiveDateTime>,
 | 
			
		||||
}
 | 
			
		||||
@@ -51,7 +50,6 @@ impl Cipher {
 | 
			
		||||
            organization_uuid: None,
 | 
			
		||||
 | 
			
		||||
            atype,
 | 
			
		||||
            favorite: false,
 | 
			
		||||
            name,
 | 
			
		||||
 | 
			
		||||
            notes: None,
 | 
			
		||||
@@ -128,7 +126,7 @@ impl Cipher {
 | 
			
		||||
            "RevisionDate": format_date(&self.updated_at),
 | 
			
		||||
            "DeletedDate": self.deleted_at.map_or(Value::Null, |d| Value::String(format_date(&d))),
 | 
			
		||||
            "FolderId": self.get_folder_uuid(&user_uuid, &conn),
 | 
			
		||||
            "Favorite": self.favorite,
 | 
			
		||||
            "Favorite": self.is_favorite(&user_uuid, &conn),
 | 
			
		||||
            "OrganizationId": self.organization_uuid,
 | 
			
		||||
            "Attachments": attachments_json,
 | 
			
		||||
            "OrganizationUseTotp": true,
 | 
			
		||||
@@ -337,6 +335,50 @@ impl Cipher {
 | 
			
		||||
        self.get_access_restrictions(&user_uuid, &conn).is_some()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Returns whether this cipher is a favorite of the specified user.
 | 
			
		||||
    pub fn is_favorite(&self, user_uuid: &str, conn: &DbConn) -> bool {
 | 
			
		||||
        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.
 | 
			
		||||
    pub fn set_favorite(&self, favorite: Option<bool>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
 | 
			
		||||
        if favorite.is_none() {
 | 
			
		||||
            // No change requested.
 | 
			
		||||
            return Ok(());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let (old, new) = (self.is_favorite(user_uuid, &conn), favorite.unwrap());
 | 
			
		||||
        match (old, new) {
 | 
			
		||||
            (false, true) => {
 | 
			
		||||
                User::update_uuid_revision(user_uuid, &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);
 | 
			
		||||
                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(())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_folder_uuid(&self, user_uuid: &str, conn: &DbConn) -> Option<String> {
 | 
			
		||||
        folders_ciphers::table
 | 
			
		||||
            .inner_join(folders::table)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
table! {
 | 
			
		||||
    attachments (id) {
 | 
			
		||||
        id -> Varchar,
 | 
			
		||||
        cipher_uuid -> Varchar,
 | 
			
		||||
        id -> Text,
 | 
			
		||||
        cipher_uuid -> Text,
 | 
			
		||||
        file_name -> Text,
 | 
			
		||||
        file_size -> Integer,
 | 
			
		||||
        akey -> Nullable<Text>,
 | 
			
		||||
@@ -10,17 +10,16 @@ table! {
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    ciphers (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        created_at -> Datetime,
 | 
			
		||||
        updated_at -> Datetime,
 | 
			
		||||
        user_uuid -> Nullable<Varchar>,
 | 
			
		||||
        organization_uuid -> Nullable<Varchar>,
 | 
			
		||||
        user_uuid -> Nullable<Text>,
 | 
			
		||||
        organization_uuid -> Nullable<Text>,
 | 
			
		||||
        atype -> Integer,
 | 
			
		||||
        name -> Text,
 | 
			
		||||
        notes -> Nullable<Text>,
 | 
			
		||||
        fields -> Nullable<Text>,
 | 
			
		||||
        data -> Text,
 | 
			
		||||
        favorite -> Bool,
 | 
			
		||||
        password_history -> Nullable<Text>,
 | 
			
		||||
        deleted_at -> Nullable<Datetime>,
 | 
			
		||||
    }
 | 
			
		||||
@@ -28,25 +27,25 @@ table! {
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    ciphers_collections (cipher_uuid, collection_uuid) {
 | 
			
		||||
        cipher_uuid -> Varchar,
 | 
			
		||||
        collection_uuid -> Varchar,
 | 
			
		||||
        cipher_uuid -> Text,
 | 
			
		||||
        collection_uuid -> Text,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    collections (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        org_uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        org_uuid -> Text,
 | 
			
		||||
        name -> Text,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    devices (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        created_at -> Datetime,
 | 
			
		||||
        updated_at -> Datetime,
 | 
			
		||||
        user_uuid -> Varchar,
 | 
			
		||||
        user_uuid -> Text,
 | 
			
		||||
        name -> Text,
 | 
			
		||||
        atype -> Integer,
 | 
			
		||||
        push_token -> Nullable<Text>,
 | 
			
		||||
@@ -55,33 +54,40 @@ table! {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    favorites (user_uuid, cipher_uuid) {
 | 
			
		||||
        user_uuid -> Text,
 | 
			
		||||
        cipher_uuid -> Text,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    folders (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        created_at -> Datetime,
 | 
			
		||||
        updated_at -> Datetime,
 | 
			
		||||
        user_uuid -> Varchar,
 | 
			
		||||
        user_uuid -> Text,
 | 
			
		||||
        name -> Text,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    folders_ciphers (cipher_uuid, folder_uuid) {
 | 
			
		||||
        cipher_uuid -> Varchar,
 | 
			
		||||
        folder_uuid -> Varchar,
 | 
			
		||||
        cipher_uuid -> Text,
 | 
			
		||||
        folder_uuid -> Text,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    invitations (email) {
 | 
			
		||||
        email -> Varchar,
 | 
			
		||||
        email -> Text,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    org_policies (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        org_uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        org_uuid -> Text,
 | 
			
		||||
        atype -> Integer,
 | 
			
		||||
        enabled -> Bool,
 | 
			
		||||
        data -> Text,
 | 
			
		||||
@@ -90,7 +96,7 @@ table! {
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    organizations (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        name -> Text,
 | 
			
		||||
        billing_email -> Text,
 | 
			
		||||
    }
 | 
			
		||||
@@ -98,8 +104,8 @@ table! {
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    twofactor (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        user_uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        user_uuid -> Text,
 | 
			
		||||
        atype -> Integer,
 | 
			
		||||
        enabled -> Bool,
 | 
			
		||||
        data -> Text,
 | 
			
		||||
@@ -109,18 +115,18 @@ table! {
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    users (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        created_at -> Datetime,
 | 
			
		||||
        updated_at -> Datetime,
 | 
			
		||||
        verified_at -> Nullable<Datetime>,
 | 
			
		||||
        last_verifying_at -> Nullable<Datetime>,
 | 
			
		||||
        login_verify_count -> Integer,
 | 
			
		||||
        email -> Varchar,
 | 
			
		||||
        email_new -> Nullable<Varchar>,
 | 
			
		||||
        email_new_token -> Nullable<Varchar>,
 | 
			
		||||
        email -> Text,
 | 
			
		||||
        email_new -> Nullable<Text>,
 | 
			
		||||
        email_new_token -> Nullable<Text>,
 | 
			
		||||
        name -> Text,
 | 
			
		||||
        password_hash -> Blob,
 | 
			
		||||
        salt -> Blob,
 | 
			
		||||
        password_hash -> Binary,
 | 
			
		||||
        salt -> Binary,
 | 
			
		||||
        password_iterations -> Integer,
 | 
			
		||||
        password_hint -> Nullable<Text>,
 | 
			
		||||
        akey -> Text,
 | 
			
		||||
@@ -138,8 +144,8 @@ table! {
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    users_collections (user_uuid, collection_uuid) {
 | 
			
		||||
        user_uuid -> Varchar,
 | 
			
		||||
        collection_uuid -> Varchar,
 | 
			
		||||
        user_uuid -> Text,
 | 
			
		||||
        collection_uuid -> Text,
 | 
			
		||||
        read_only -> Bool,
 | 
			
		||||
        hide_passwords -> Bool,
 | 
			
		||||
    }
 | 
			
		||||
@@ -147,9 +153,9 @@ table! {
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    users_organizations (uuid) {
 | 
			
		||||
        uuid -> Varchar,
 | 
			
		||||
        user_uuid -> Varchar,
 | 
			
		||||
        org_uuid -> Varchar,
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
        user_uuid -> Text,
 | 
			
		||||
        org_uuid -> Text,
 | 
			
		||||
        access_all -> Bool,
 | 
			
		||||
        akey -> Text,
 | 
			
		||||
        status -> Integer,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@ table! {
 | 
			
		||||
        notes -> Nullable<Text>,
 | 
			
		||||
        fields -> Nullable<Text>,
 | 
			
		||||
        data -> Text,
 | 
			
		||||
        favorite -> Bool,
 | 
			
		||||
        password_history -> Nullable<Text>,
 | 
			
		||||
        deleted_at -> Nullable<Timestamp>,
 | 
			
		||||
    }
 | 
			
		||||
@@ -55,6 +54,13 @@ table! {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    favorites (user_uuid, cipher_uuid) {
 | 
			
		||||
        user_uuid -> Text,
 | 
			
		||||
        cipher_uuid -> Text,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    folders (uuid) {
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@ table! {
 | 
			
		||||
        notes -> Nullable<Text>,
 | 
			
		||||
        fields -> Nullable<Text>,
 | 
			
		||||
        data -> Text,
 | 
			
		||||
        favorite -> Bool,
 | 
			
		||||
        password_history -> Nullable<Text>,
 | 
			
		||||
        deleted_at -> Nullable<Timestamp>,
 | 
			
		||||
    }
 | 
			
		||||
@@ -55,6 +54,13 @@ table! {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    favorites (user_uuid, cipher_uuid) {
 | 
			
		||||
        user_uuid -> Text,
 | 
			
		||||
        cipher_uuid -> Text,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    folders (uuid) {
 | 
			
		||||
        uuid -> Text,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -313,12 +313,23 @@ mod migrations {
 | 
			
		||||
 | 
			
		||||
        // Disable Foreign Key Checks during migration
 | 
			
		||||
        use diesel::RunQueryDsl;
 | 
			
		||||
 | 
			
		||||
        // FIXME: Per https://www.postgresql.org/docs/12/sql-set-constraints.html,
 | 
			
		||||
        // "SET CONSTRAINTS sets the behavior of constraint checking within the
 | 
			
		||||
        // current transaction", so this setting probably won't take effect for
 | 
			
		||||
        // any of the migrations since it's being run outside of a transaction.
 | 
			
		||||
        // Migrations that need to disable foreign key checks should run this
 | 
			
		||||
        // from within the migration script itself.
 | 
			
		||||
        #[cfg(feature = "postgres")]
 | 
			
		||||
        diesel::sql_query("SET CONSTRAINTS ALL DEFERRED").execute(&connection).expect("Failed to disable Foreign Key Checks during migrations");
 | 
			
		||||
 | 
			
		||||
        // Scoped to a connection/session.
 | 
			
		||||
        #[cfg(feature = "mysql")]
 | 
			
		||||
        diesel::sql_query("SET FOREIGN_KEY_CHECKS = 0").execute(&connection).expect("Failed to disable Foreign Key Checks during migrations");
 | 
			
		||||
 | 
			
		||||
        // Scoped to a connection.
 | 
			
		||||
        #[cfg(feature = "sqlite")]
 | 
			
		||||
        diesel::sql_query("PRAGMA defer_foreign_keys = ON").execute(&connection).expect("Failed to disable Foreign Key Checks during migrations");
 | 
			
		||||
        diesel::sql_query("PRAGMA foreign_keys = OFF").execute(&connection).expect("Failed to disable Foreign Key Checks during migrations");
 | 
			
		||||
 | 
			
		||||
        embedded_migrations::run_with_output(&connection, &mut stdout()).expect("Can't run migrations");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user