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

folder.rs (6534B)


      1 use super::User;
      2 use crate::util;
      3 use chrono::{NaiveDateTime, Utc};
      4 use diesel::result::{self, DatabaseErrorKind};
      5 use serde_json::Value;
      6 
      7 db_object! {
      8     #[derive(AsChangeset, Insertable, Queryable)]
      9     #[diesel(table_name = folders)]
     10     pub struct Folder {
     11         pub uuid: String,
     12         created_at: NaiveDateTime,
     13         pub updated_at: NaiveDateTime,
     14         pub user_uuid: String,
     15         pub name: String,
     16     }
     17 
     18     #[derive(Insertable, Queryable)]
     19     #[diesel(table_name = folders_ciphers)]
     20     pub struct FolderCipher {
     21         cipher_uuid: String,
     22         folder_uuid: String,
     23     }
     24 }
     25 
     26 /// Local methods
     27 impl Folder {
     28     pub fn new(user_uuid: String, name: String) -> Self {
     29         let now = Utc::now().naive_utc();
     30         Self {
     31             uuid: util::get_uuid(),
     32             created_at: now,
     33             updated_at: now,
     34 
     35             user_uuid,
     36             name,
     37         }
     38     }
     39     pub fn to_json(&self) -> Value {
     40         use util::format_date;
     41         json!({
     42             "id": self.uuid,
     43             "revisionDate": format_date(&self.updated_at),
     44             "name": self.name,
     45             "object": "folder",
     46         })
     47     }
     48 }
     49 
     50 impl FolderCipher {
     51     pub fn new(folder_uuid: &str, cipher_uuid: &str) -> Self {
     52         Self {
     53             folder_uuid: folder_uuid.to_owned(),
     54             cipher_uuid: cipher_uuid.to_owned(),
     55         }
     56     }
     57 }
     58 
     59 use crate::api::EmptyResult;
     60 use crate::db::DbConn;
     61 use crate::error::MapResult;
     62 
     63 /// Database methods
     64 impl Folder {
     65     pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
     66         User::update_uuid_revision(&self.user_uuid, conn).await;
     67         self.updated_at = Utc::now().naive_utc();
     68         db_run! { conn:
     69             {
     70                 match diesel::replace_into(folders::table)
     71                     .values(FolderDb::to_db(self))
     72                     .execute(conn)
     73                 {
     74                     Ok(_) => Ok(()),
     75                     // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
     76                     Err(result::Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)) => {
     77                         diesel::update(folders::table)
     78                             .filter(folders::uuid.eq(&self.uuid))
     79                             .set(FolderDb::to_db(self))
     80                             .execute(conn)
     81                             .map_res("Error saving folder")
     82                     }
     83                     Err(e) => Err(e.into()),
     84                 }.map_res("Error saving folder")
     85             }
     86         }
     87     }
     88 
     89     pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
     90         User::update_uuid_revision(&self.user_uuid, conn).await;
     91         FolderCipher::delete_all_by_folder(&self.uuid, conn).await?;
     92         db_run! { conn: {
     93             diesel::delete(folders::table.filter(folders::uuid.eq(&self.uuid)))
     94                 .execute(conn)
     95                 .map_res("Error deleting folder")
     96         }}
     97     }
     98 
     99     pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
    100         for folder in Self::find_by_user(user_uuid, conn).await {
    101             folder.delete(conn).await?;
    102         }
    103         Ok(())
    104     }
    105 
    106     pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
    107         db_run! { conn: {
    108             folders::table
    109                 .filter(folders::uuid.eq(uuid))
    110                 .first::<FolderDb>(conn)
    111                 .ok()
    112                 .from_db()
    113         }}
    114     }
    115 
    116     pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
    117         db_run! { conn: {
    118             folders::table
    119                 .filter(folders::user_uuid.eq(user_uuid))
    120                 .load::<FolderDb>(conn)
    121                 .expect("Error loading folders")
    122                 .from_db()
    123         }}
    124     }
    125 }
    126 
    127 impl FolderCipher {
    128     pub async fn save(&self, conn: &DbConn) -> EmptyResult {
    129         db_run! { conn:
    130             {
    131                 // Not checking for ForeignKey Constraints here.
    132                 // Table folders_ciphers does not have ForeignKey Constraints which would cause conflicts.
    133                 // This table has no constraints pointing to itself, but only to others.
    134                 diesel::replace_into(folders_ciphers::table)
    135                     .values(FolderCipherDb::to_db(self))
    136                     .execute(conn)
    137                     .map_res("Error adding cipher to folder")
    138             }
    139         }
    140     }
    141 
    142     pub async fn delete(self, conn: &DbConn) -> EmptyResult {
    143         db_run! { conn: {
    144             diesel::delete(
    145                 folders_ciphers::table
    146                     .filter(folders_ciphers::cipher_uuid.eq(self.cipher_uuid))
    147                     .filter(folders_ciphers::folder_uuid.eq(self.folder_uuid)),
    148             )
    149             .execute(conn)
    150             .map_res("Error removing cipher from folder")
    151         }}
    152     }
    153 
    154     pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
    155         db_run! { conn: {
    156             diesel::delete(folders_ciphers::table.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid)))
    157                 .execute(conn)
    158                 .map_res("Error removing cipher from folders")
    159         }}
    160     }
    161 
    162     async fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> EmptyResult {
    163         db_run! { conn: {
    164             diesel::delete(folders_ciphers::table.filter(folders_ciphers::folder_uuid.eq(folder_uuid)))
    165                 .execute(conn)
    166                 .map_res("Error removing ciphers from folder")
    167         }}
    168     }
    169 
    170     pub async fn find_by_folder_and_cipher(
    171         folder_uuid: &str,
    172         cipher_uuid: &str,
    173         conn: &DbConn,
    174     ) -> Option<Self> {
    175         db_run! { conn: {
    176             folders_ciphers::table
    177                 .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
    178                 .filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))
    179                 .first::<FolderCipherDb>(conn)
    180                 .ok()
    181                 .from_db()
    182         }}
    183     }
    184     /// Return a vec with (cipher_uuid, folder_uuid)
    185     /// This is used during a full sync so we only need one query for all folder matches.
    186     pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<(String, String)> {
    187         db_run! { conn: {
    188             folders_ciphers::table
    189                 .inner_join(folders::table)
    190                 .filter(folders::user_uuid.eq(user_uuid))
    191                 .select(folders_ciphers::all_columns)
    192                 .load::<(String, String)>(conn)
    193                 .unwrap_or_default()
    194         }}
    195     }
    196 }