mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-26 07:50:02 +02:00 
			
		
		
		
	Merge pull request #12 from mprasil/user_decoupling
Decoupling user from cipher
This commit is contained in:
		
							
								
								
									
										32
									
								
								migrations/2018-04-27-155151_create_users_ciphers/up.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								migrations/2018-04-27-155151_create_users_ciphers/up.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| ALTER TABLE ciphers RENAME TO oldCiphers; | ||||
|  | ||||
| CREATE TABLE ciphers ( | ||||
|   uuid              TEXT     NOT NULL PRIMARY KEY, | ||||
|   created_at        DATETIME NOT NULL, | ||||
|   updated_at        DATETIME NOT NULL, | ||||
|   user_uuid         TEXT     REFERENCES users (uuid), -- Make this optional | ||||
|   organization_uuid TEXT     REFERENCES organizations (uuid), -- Add reference to orgs table | ||||
|   -- Remove folder_uuid | ||||
|   type              INTEGER  NOT NULL, | ||||
|   name              TEXT     NOT NULL, | ||||
|   notes             TEXT, | ||||
|   fields            TEXT, | ||||
|   data              TEXT     NOT NULL, | ||||
|   favorite          BOOLEAN  NOT NULL | ||||
| ); | ||||
|  | ||||
| CREATE TABLE folders_ciphers ( | ||||
|   cipher_uuid TEXT NOT NULL REFERENCES ciphers (uuid), | ||||
|   folder_uuid TEXT NOT NULL REFERENCES folders (uuid), | ||||
|  | ||||
|   PRIMARY KEY (cipher_uuid, folder_uuid) | ||||
| ); | ||||
|  | ||||
| INSERT INTO ciphers (uuid, created_at, updated_at, user_uuid, organization_uuid, type, name, notes, fields, data, favorite)  | ||||
| SELECT uuid, created_at, updated_at, user_uuid, organization_uuid, type, name, notes, fields, data, favorite FROM oldCiphers; | ||||
|  | ||||
| INSERT INTO folders_ciphers (cipher_uuid, folder_uuid) | ||||
| SELECT uuid, folder_uuid FROM oldCiphers WHERE folder_uuid IS NOT NULL; | ||||
|  | ||||
|  | ||||
| DROP TABLE oldCiphers; | ||||
| @@ -29,7 +29,7 @@ fn sync(headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     let folders_json: Vec<Value> = folders.iter().map(|c| c.to_json()).collect(); | ||||
|  | ||||
|     let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn); | ||||
|     let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &conn)).collect(); | ||||
|     let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect(); | ||||
|  | ||||
|     let domains_json = api::core::get_eq_domains(headers).unwrap().into_inner(); | ||||
|  | ||||
| @@ -47,7 +47,7 @@ fn sync(headers: Headers, conn: DbConn) -> JsonResult { | ||||
| fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn); | ||||
|  | ||||
|     let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &conn)).collect(); | ||||
|     let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect(); | ||||
|  | ||||
|     Ok(Json(json!({ | ||||
|       "Data": ciphers_json, | ||||
| @@ -62,11 +62,11 @@ fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|         None => err!("Cipher doesn't exist") | ||||
|     }; | ||||
|  | ||||
|     if cipher.user_uuid != headers.user.uuid { | ||||
|     if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) { | ||||
|         err!("Cipher is not owned by user") | ||||
|     } | ||||
|  | ||||
|     Ok(Json(cipher.to_json(&headers.host, &conn))) | ||||
|     Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Debug)] | ||||
| @@ -109,12 +109,12 @@ fn post_ciphers(data: Json<CipherData>, headers: Headers, conn: DbConn) -> JsonR | ||||
|  | ||||
|     let user_uuid = headers.user.uuid.clone(); | ||||
|     let favorite = data.favorite.unwrap_or(false); | ||||
|     let mut cipher = Cipher::new(user_uuid, data.type_, data.name.clone(), favorite); | ||||
|     let mut cipher = Cipher::new(Some(user_uuid), None, data.type_, data.name.clone(), favorite); | ||||
|  | ||||
|     update_cipher_from_data(&mut cipher, data, &headers, &conn)?; | ||||
|     cipher.save(&conn); | ||||
|  | ||||
|     Ok(Json(cipher.to_json(&headers.host, &conn))) | ||||
|     Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) | ||||
| } | ||||
|  | ||||
| fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &Headers, conn: &DbConn) -> EmptyResult { | ||||
| @@ -174,7 +174,9 @@ fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &Head | ||||
|     // Copy the type data and change the names to the correct case | ||||
|     copy_values(&type_data, &mut values); | ||||
|  | ||||
|     cipher.folder_uuid = data.folderId; | ||||
|     if cipher.move_to_folder(data.folderId, &headers.user.uuid, &conn).is_err() { | ||||
|         err!("Error saving the folder information") | ||||
|     } | ||||
|     cipher.name = data.name; | ||||
|     cipher.notes = data.notes; | ||||
|     cipher.fields = uppercase_fields.map(|f| f.to_string()); | ||||
| @@ -247,12 +249,11 @@ fn post_ciphers_import(data: Json<ImportData>, headers: Headers, conn: DbConn) - | ||||
|  | ||||
|         let user_uuid = headers.user.uuid.clone(); | ||||
|         let favorite = cipher_data.favorite.unwrap_or(false); | ||||
|         let mut cipher = Cipher::new(user_uuid, cipher_data.type_, cipher_data.name.clone(), favorite); | ||||
|         let mut cipher = Cipher::new(Some(user_uuid), None, cipher_data.type_, cipher_data.name.clone(), favorite); | ||||
|  | ||||
|         if update_cipher_from_data(&mut cipher, cipher_data, &headers, &conn).is_err() { err!("Error creating cipher") } | ||||
|  | ||||
|         cipher.folder_uuid = folder_uuid; | ||||
|  | ||||
|         cipher.move_to_folder(folder_uuid, &headers.user.uuid.clone(), &conn).ok(); | ||||
|         cipher.save(&conn); | ||||
|         index += 1; | ||||
|     } | ||||
| @@ -274,8 +275,8 @@ fn put_cipher(uuid: String, data: Json<CipherData>, headers: Headers, conn: DbCo | ||||
|         None => err!("Cipher doesn't exist") | ||||
|     }; | ||||
|  | ||||
|     if cipher.user_uuid != headers.user.uuid { | ||||
|         err!("Cipher is not owned by user") | ||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) { | ||||
|         err!("Cipher is not write accessible") | ||||
|     } | ||||
|  | ||||
|     cipher.favorite = data.favorite.unwrap_or(false); | ||||
| @@ -283,7 +284,7 @@ fn put_cipher(uuid: String, data: Json<CipherData>, headers: Headers, conn: DbCo | ||||
|     update_cipher_from_data(&mut cipher, data, &headers, &conn)?; | ||||
|     cipher.save(&conn); | ||||
|  | ||||
|     Ok(Json(cipher.to_json(&headers.host, &conn))) | ||||
|     Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -294,8 +295,8 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers | ||||
|         None => err!("Cipher doesn't exist") | ||||
|     }; | ||||
|  | ||||
|     if cipher.user_uuid != headers.user.uuid { | ||||
|         err!("Cipher is not owned by user") | ||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) { | ||||
|         err!("Cipher is not write accessible") | ||||
|     } | ||||
|  | ||||
|     let mut params = content_type.params(); | ||||
| @@ -323,7 +324,7 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers | ||||
|         attachment.save(&conn); | ||||
|     }).expect("Error processing multipart data"); | ||||
|  | ||||
|     Ok(Json(cipher.to_json(&headers.host, &conn))) | ||||
|     Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) | ||||
| } | ||||
|  | ||||
| #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")] | ||||
| @@ -347,8 +348,8 @@ fn delete_attachment(uuid: String, attachment_id: String, headers: Headers, conn | ||||
|         None => err!("Cipher doesn't exist") | ||||
|     }; | ||||
|  | ||||
|     if cipher.user_uuid != headers.user.uuid { | ||||
|         err!("Cipher is not owned by user") | ||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) { | ||||
|         err!("Cipher cannot be deleted by user") | ||||
|     } | ||||
|  | ||||
|     // Delete attachment | ||||
| @@ -399,7 +400,7 @@ fn move_cipher_selected(data: Json<Value>, headers: Headers, conn: DbConn) -> Em | ||||
|                             if folder.user_uuid != headers.user.uuid { | ||||
|                                 err!("Folder is not owned by user") | ||||
|                             } | ||||
|                             Some(folder_id.to_string()) | ||||
|                             Some(folder.uuid) | ||||
|                         } | ||||
|                         None => err!("Folder doesn't exist") | ||||
|                     } | ||||
| @@ -424,12 +425,14 @@ fn move_cipher_selected(data: Json<Value>, headers: Headers, conn: DbConn) -> Em | ||||
|             None => err!("Cipher doesn't exist") | ||||
|         }; | ||||
|  | ||||
|         if cipher.user_uuid != headers.user.uuid { | ||||
|             err!("Cipher is not owned by user") | ||||
|         if !cipher.is_accessible_to_user(&headers.user.uuid, &conn) { | ||||
|             err!("Cipher is not accessible by user") | ||||
|         } | ||||
|  | ||||
|         // Move cipher | ||||
|         cipher.folder_uuid = folder_id.clone(); | ||||
|         if cipher.move_to_folder(folder_id.clone(), &headers.user.uuid, &conn).is_err() { | ||||
|             err!("Error saving the folder information") | ||||
|         } | ||||
|         cipher.save(&conn); | ||||
|     } | ||||
|  | ||||
| @@ -464,8 +467,8 @@ fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn) -> Empty | ||||
|         None => err!("Cipher doesn't exist"), | ||||
|     }; | ||||
|  | ||||
|     if cipher.user_uuid != headers.user.uuid { | ||||
|         err!("Cipher is not owned by user") | ||||
|     if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) { | ||||
|         err!("Cipher can't be deleted by user") | ||||
|     } | ||||
|  | ||||
|     _delete_cipher(cipher, conn); | ||||
|   | ||||
| @@ -109,6 +109,7 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     Ok(Json(json!({ | ||||
|         "Data": | ||||
|             Collection::find_by_user_uuid(&headers.user.uuid, &conn) | ||||
|             .expect("Error loading collections") | ||||
|             .iter() | ||||
|             .map(|collection| { | ||||
|                 collection.to_json() | ||||
| @@ -121,9 +122,9 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult { | ||||
| fn get_org_collections(org_id: String, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     Ok(Json(json!({ | ||||
|         "Data": | ||||
|             Collection::find_by_user_uuid(&headers.user.uuid, &conn) | ||||
|             Collection::find_by_organization_and_user_uuid(&org_id, &headers.user.uuid, &conn) | ||||
|             .unwrap_or(vec![]) | ||||
|             .iter() | ||||
|             .filter(|collection| { collection.org_uuid == org_id }) | ||||
|             .map(|collection| { | ||||
|                 collection.to_json() | ||||
|             }).collect::<Value>(), | ||||
| @@ -230,7 +231,7 @@ struct OrgIdData { | ||||
| #[get("/ciphers/organization-details?<data>")] | ||||
| fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     let ciphers = Cipher::find_by_org(&data.organizationId, &conn); | ||||
|     let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &conn)).collect(); | ||||
|     let ciphers_json: Vec<Value> = ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect(); | ||||
|  | ||||
|     Ok(Json(json!({ | ||||
|       "Data": ciphers_json, | ||||
|   | ||||
| @@ -3,19 +3,19 @@ use serde_json::Value as JsonValue; | ||||
|  | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use super::User; | ||||
| use super::{User, Organization, UserOrganization, FolderCipher}; | ||||
|  | ||||
| #[derive(Debug, Identifiable, Queryable, Insertable, Associations)] | ||||
| #[table_name = "ciphers"] | ||||
| #[belongs_to(User, foreign_key = "user_uuid")] | ||||
| #[belongs_to(Organization, foreign_key = "organization_uuid")] | ||||
| #[primary_key(uuid)] | ||||
| pub struct Cipher { | ||||
|     pub uuid: String, | ||||
|     pub created_at: NaiveDateTime, | ||||
|     pub updated_at: NaiveDateTime, | ||||
|  | ||||
|     pub user_uuid: String, | ||||
|     pub folder_uuid: Option<String>, | ||||
|     pub user_uuid: Option<String>, | ||||
|     pub organization_uuid: Option<String>, | ||||
|  | ||||
|     /* | ||||
| @@ -36,7 +36,7 @@ pub struct Cipher { | ||||
|  | ||||
| /// Local methods | ||||
| impl Cipher { | ||||
|     pub fn new(user_uuid: String, type_: i32, name: String, favorite: bool) -> Self { | ||||
|     pub fn new(user_uuid: Option<String>, organization_uuid: Option<String>, type_: i32, name: String, favorite: bool) -> Self { | ||||
|         let now = Utc::now().naive_utc(); | ||||
|  | ||||
|         Self { | ||||
| @@ -45,8 +45,7 @@ impl Cipher { | ||||
|             updated_at: now, | ||||
|  | ||||
|             user_uuid, | ||||
|             folder_uuid: None, | ||||
|             organization_uuid: None, | ||||
|             organization_uuid, | ||||
|  | ||||
|             type_, | ||||
|             favorite, | ||||
| @@ -63,11 +62,11 @@ impl Cipher { | ||||
| use diesel; | ||||
| use diesel::prelude::*; | ||||
| use db::DbConn; | ||||
| use db::schema::ciphers; | ||||
| use db::schema::*; | ||||
|  | ||||
| /// Database methods | ||||
| impl Cipher { | ||||
|     pub fn to_json(&self, host: &str, conn: &DbConn) -> JsonValue { | ||||
|     pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> JsonValue { | ||||
|         use serde_json; | ||||
|         use util::format_date; | ||||
|         use super::Attachment; | ||||
| @@ -94,7 +93,7 @@ impl Cipher { | ||||
|             "Id": self.uuid, | ||||
|             "Type": self.type_, | ||||
|             "RevisionDate": format_date(&self.updated_at), | ||||
|             "FolderId": self.folder_uuid, | ||||
|             "FolderId": self.get_folder_uuid(&user_uuid, &conn), | ||||
|             "Favorite": self.favorite, | ||||
|             "OrganizationId": self.organization_uuid, | ||||
|             "Attachments": attachments_json, | ||||
| @@ -142,6 +141,81 @@ impl Cipher { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> Result<(), &str> { | ||||
|         match self.get_folder_uuid(&user_uuid, &conn)  { | ||||
|             None => { | ||||
|                 match folder_uuid { | ||||
|                     Some(new_folder) => { | ||||
|                         let folder_cipher = FolderCipher::new(&new_folder, &self.uuid); | ||||
|                         folder_cipher.save(&conn).or(Err("Couldn't save folder setting")) | ||||
|                     }, | ||||
|                     None => Ok(()) //nothing to do | ||||
|                 } | ||||
|             }, | ||||
|             Some(current_folder) => { | ||||
|                 match folder_uuid { | ||||
|                     Some(new_folder) => { | ||||
|                         if current_folder == new_folder { | ||||
|                             Ok(()) //nothing to do | ||||
|                         } else { | ||||
|                             match FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) { | ||||
|                                 Some(current_folder) => { | ||||
|                                     current_folder.delete(&conn).or(Err("Failed removing old folder mapping")) | ||||
|                                 }, | ||||
|                                 None => Ok(()) // Weird, but nothing to do | ||||
|                             }.and_then( | ||||
|                                 |()| FolderCipher::new(&new_folder, &self.uuid) | ||||
|                                 .save(&conn).or(Err("Couldn't save folder setting")) | ||||
|                             ) | ||||
|                         } | ||||
|                     }, | ||||
|                     None => { | ||||
|                         match FolderCipher::find_by_folder_and_cipher(¤t_folder, &self.uuid, &conn) { | ||||
|                             Some(current_folder) => { | ||||
|                                 current_folder.delete(&conn).or(Err("Failed removing old folder mapping")) | ||||
|                             }, | ||||
|                             None => Err("Couldn't move from previous folder") | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool { | ||||
|         match self.user_uuid { | ||||
|             Some(ref self_user_uuid) => self_user_uuid == user_uuid, // cipher directly owned by user | ||||
|             None =>{ | ||||
|                 match self.organization_uuid { | ||||
|                     Some(ref org_uuid) => { | ||||
|                         match users_organizations::table | ||||
|                         .filter(users_organizations::org_uuid.eq(org_uuid)) | ||||
|                         .filter(users_organizations::user_uuid.eq(user_uuid)) | ||||
|                         .filter(users_organizations::access_all.eq(true)) | ||||
|                         .first::<UserOrganization>(&**conn).ok() { | ||||
|                             Some(_) => true, | ||||
|                             None => false //TODO R/W access on collection | ||||
|                         } | ||||
|                     }, | ||||
|                     None => false // cipher not in organization and not owned by user | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn is_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool { | ||||
|         // TODO also check for read-only access | ||||
|         self.is_write_accessible_to_user(user_uuid, conn) | ||||
|     } | ||||
|  | ||||
|     pub fn get_folder_uuid(&self, user_uuid: &str, conn: &DbConn) -> Option<String> { | ||||
|         folders_ciphers::table.inner_join(folders::table) | ||||
|             .filter(folders::user_uuid.eq(&user_uuid)) | ||||
|             .filter(folders_ciphers::cipher_uuid.eq(&self.uuid)) | ||||
|             .select(folders_ciphers::folder_uuid) | ||||
|             .first::<String>(&**conn).ok() | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { | ||||
|         ciphers::table | ||||
|             .filter(ciphers::uuid.eq(uuid)) | ||||
| @@ -161,8 +235,9 @@ impl Cipher { | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec<Self> { | ||||
|         ciphers::table | ||||
|             .filter(ciphers::folder_uuid.eq(folder_uuid)) | ||||
|         folders_ciphers::table.inner_join(ciphers::table) | ||||
|             .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) | ||||
|             .select(ciphers::all_columns) | ||||
|             .load::<Self>(&**conn).expect("Error loading ciphers") | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -66,11 +66,19 @@ impl Collection { | ||||
|             .first::<Self>(&**conn).ok() | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec<Self> { | ||||
|     pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Option<Vec<Self>> { | ||||
|         users_collections::table.inner_join(collections::table) | ||||
|             .filter(users_collections::user_uuid.eq(user_uuid)) | ||||
|             .select(collections::all_columns) | ||||
|             .load::<Self>(&**conn).expect("Error loading user collections") | ||||
|             .load::<Self>(&**conn).ok() | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Vec<Self>> { | ||||
|         users_collections::table.inner_join(collections::table) | ||||
|             .filter(users_collections::user_uuid.eq(user_uuid)) | ||||
|             .filter(collections::org_uuid.eq(org_uuid)) | ||||
|             .select(collections::all_columns) | ||||
|             .load::<Self>(&**conn).ok() | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ use serde_json::Value as JsonValue; | ||||
|  | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use super::User; | ||||
| use super::{User, Cipher}; | ||||
|  | ||||
| #[derive(Debug, Identifiable, Queryable, Insertable, Associations)] | ||||
| #[table_name = "folders"] | ||||
| @@ -17,6 +17,16 @@ pub struct Folder { | ||||
|     pub name: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Identifiable, Queryable, Insertable, Associations)] | ||||
| #[table_name = "folders_ciphers"] | ||||
| #[belongs_to(Cipher, foreign_key = "cipher_uuid")] | ||||
| #[belongs_to(Folder, foreign_key = "folder_uuid")] | ||||
| #[primary_key(cipher_uuid, folder_uuid)] | ||||
| pub struct FolderCipher { | ||||
|     pub cipher_uuid: String, | ||||
|     pub folder_uuid: String, | ||||
| } | ||||
|  | ||||
| /// Local methods | ||||
| impl Folder { | ||||
|     pub fn new(user_uuid: String, name: String) -> Self { | ||||
| @@ -44,10 +54,19 @@ impl Folder { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl FolderCipher { | ||||
|     pub fn new(folder_uuid: &str, cipher_uuid: &str) -> Self { | ||||
|         Self { | ||||
|             folder_uuid: folder_uuid.to_string(), | ||||
|             cipher_uuid: cipher_uuid.to_string(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| use diesel; | ||||
| use diesel::prelude::*; | ||||
| use db::DbConn; | ||||
| use db::schema::folders; | ||||
| use db::schema::{folders, folders_ciphers}; | ||||
|  | ||||
| /// Database methods | ||||
| impl Folder { | ||||
| @@ -83,3 +102,25 @@ impl Folder { | ||||
|             .load::<Self>(&**conn).expect("Error loading folders") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl FolderCipher { | ||||
|     pub fn save(&self, conn: &DbConn) -> QueryResult<()> { | ||||
|         diesel::replace_into(folders_ciphers::table) | ||||
|         .values(&*self) | ||||
|         .execute(&**conn).and(Ok(())) | ||||
|     } | ||||
|  | ||||
|     pub fn delete(self, conn: &DbConn) -> QueryResult<()> { | ||||
|         diesel::delete(folders_ciphers::table | ||||
|             .filter(folders_ciphers::cipher_uuid.eq(self.cipher_uuid)) | ||||
|             .filter(folders_ciphers::folder_uuid.eq(self.folder_uuid)) | ||||
|         ).execute(&**conn).and(Ok(())) | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option<Self> { | ||||
|         folders_ciphers::table | ||||
|             .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) | ||||
|             .filter(folders_ciphers::cipher_uuid.eq(cipher_uuid)) | ||||
|             .first::<Self>(&**conn).ok() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -10,7 +10,7 @@ mod organization; | ||||
| pub use self::attachment::Attachment; | ||||
| pub use self::cipher::Cipher; | ||||
| pub use self::device::Device; | ||||
| pub use self::folder::Folder; | ||||
| pub use self::folder::{Folder, FolderCipher}; | ||||
| pub use self::user::User; | ||||
| pub use self::organization::Organization; | ||||
| pub use self::organization::{UserOrganization, UserOrgStatus, UserOrgType}; | ||||
|   | ||||
| @@ -225,7 +225,7 @@ impl UserOrganization { | ||||
|     pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { | ||||
|         users_organizations::table | ||||
|             .filter(users_organizations::user_uuid.eq(user_uuid)) | ||||
|             .load::<Self>(&**conn).expect("Error loading user organizations") | ||||
|             .load::<Self>(&**conn).unwrap_or(vec![]) | ||||
|     } | ||||
|  | ||||
|     pub fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> { | ||||
|   | ||||
| @@ -12,8 +12,7 @@ table! { | ||||
|         uuid -> Text, | ||||
|         created_at -> Timestamp, | ||||
|         updated_at -> Timestamp, | ||||
|         user_uuid -> Text, | ||||
|         folder_uuid -> Nullable<Text>, | ||||
|         user_uuid -> Nullable<Text>, | ||||
|         organization_uuid -> Nullable<Text>, | ||||
|         #[sql_name = "type"] | ||||
|         type_ -> Integer, | ||||
| @@ -107,8 +106,14 @@ table! { | ||||
|     } | ||||
| } | ||||
|  | ||||
| table! { | ||||
|     folders_ciphers (cipher_uuid, folder_uuid) { | ||||
|         cipher_uuid -> Text, | ||||
|         folder_uuid -> Text, | ||||
|     } | ||||
| } | ||||
|  | ||||
| joinable!(attachments -> ciphers (cipher_uuid)); | ||||
| joinable!(ciphers -> folders (folder_uuid)); | ||||
| joinable!(ciphers -> users (user_uuid)); | ||||
| joinable!(collections -> organizations (org_uuid)); | ||||
| joinable!(devices -> users (user_uuid)); | ||||
| @@ -117,6 +122,8 @@ joinable!(users_collections -> collections (collection_uuid)); | ||||
| joinable!(users_collections -> users (user_uuid)); | ||||
| joinable!(users_organizations -> organizations (org_uuid)); | ||||
| joinable!(users_organizations -> users (user_uuid)); | ||||
| joinable!(folders_ciphers -> ciphers (cipher_uuid)); | ||||
| joinable!(folders_ciphers -> folders (folder_uuid)); | ||||
|  | ||||
| allow_tables_to_appear_in_same_query!( | ||||
|     attachments, | ||||
| @@ -128,4 +135,5 @@ allow_tables_to_appear_in_same_query!( | ||||
|     users, | ||||
|     users_collections, | ||||
|     users_organizations, | ||||
|     folders_ciphers, | ||||
| ); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user