diff options
author | Daniel García <[email protected]> | 2020-08-18 17:15:44 +0200 |
---|---|---|
committer | Daniel García <[email protected]> | 2020-08-24 20:11:17 +0200 |
commit | 0365b7c6a4d8aa88fd9328fcc14beef300fe33a2 (patch) | |
tree | b58aec0e0e57a422b95227538faeefb30f92e288 /src/db/models/collection.rs | |
parent | 19889187a5d3f48cbe5ad7ec3a0c4d0bcdb7b894 (diff) | |
download | vaultwarden-0365b7c6a4d8aa88fd9328fcc14beef300fe33a2.tar.gz vaultwarden-0365b7c6a4d8aa88fd9328fcc14beef300fe33a2.zip |
Add support for multiple simultaneous database features by using macros.
Diesel requires the following changes:
- Separate connection and pool types per connection, the generate_connections! macro generates an enum with a variant per db type
- Separate migrations and schemas, these were always imported as one type depending on db feature, now they are all imported under different module names
- Separate model objects per connection, the db_object! macro generates one object for each connection with the diesel macros, a generic object, and methods to convert between the connection-specific and the generic ones
- Separate connection queries, the db_run! macro allows writing only one that gets compiled for all databases or multiple ones
Diffstat (limited to 'src/db/models/collection.rs')
-rw-r--r-- | src/db/models/collection.rs | 468 |
1 files changed, 252 insertions, 216 deletions
diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index 8e05fb27..18d1ff03 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -1,15 +1,39 @@ use serde_json::Value; -use super::{Organization, UserOrgStatus, UserOrgType, UserOrganization}; - -#[derive(Debug, Identifiable, Queryable, Insertable, Associations, AsChangeset)] -#[table_name = "collections"] -#[belongs_to(Organization, foreign_key = "org_uuid")] -#[primary_key(uuid)] -pub struct Collection { - pub uuid: String, - pub org_uuid: String, - pub name: String, +use super::{Organization, UserOrgStatus, UserOrgType, UserOrganization, User, Cipher}; + +db_object! { + #[derive(Debug, Identifiable, Queryable, Insertable, Associations, AsChangeset)] + #[table_name = "collections"] + #[belongs_to(Organization, foreign_key = "org_uuid")] + #[primary_key(uuid)] + pub struct Collection { + pub uuid: String, + pub org_uuid: String, + pub name: String, + } + + #[derive(Debug, Identifiable, Queryable, Insertable, Associations)] + #[table_name = "users_collections"] + #[belongs_to(User, foreign_key = "user_uuid")] + #[belongs_to(Collection, foreign_key = "collection_uuid")] + #[primary_key(user_uuid, collection_uuid)] + pub struct CollectionUser { + pub user_uuid: String, + pub collection_uuid: String, + pub read_only: bool, + pub hide_passwords: bool, + } + + #[derive(Debug, Identifiable, Queryable, Insertable, Associations)] + #[table_name = "ciphers_collections"] + #[belongs_to(Cipher, foreign_key = "cipher_uuid")] + #[belongs_to(Collection, foreign_key = "collection_uuid")] + #[primary_key(cipher_uuid, collection_uuid)] + pub struct CollectionCipher { + pub cipher_uuid: String, + pub collection_uuid: String, + } } /// Local methods @@ -33,36 +57,34 @@ impl Collection { } } -use crate::db::schema::*; use crate::db::DbConn; -use diesel::prelude::*; use crate::api::EmptyResult; use crate::error::MapResult; /// Database methods impl Collection { - #[cfg(feature = "postgresql")] pub fn save(&self, conn: &DbConn) -> EmptyResult { self.update_users_revision(conn); - diesel::insert_into(collections::table) - .values(self) - .on_conflict(collections::uuid) - .do_update() - .set(self) - .execute(&**conn) - .map_res("Error saving collection") - } - - #[cfg(not(feature = "postgresql"))] - pub fn save(&self, conn: &DbConn) -> EmptyResult { - self.update_users_revision(conn); - - diesel::replace_into(collections::table) - .values(self) - .execute(&**conn) - .map_res("Error saving collection") + db_run! { conn: + sqlite, mysql { + diesel::replace_into(collections::table) + .values(CollectionDb::to_db(self)) + .execute(conn) + .map_res("Error saving collection") + } + postgresql { + let value = CollectionDb::to_db(self); + diesel::insert_into(collections::table) + .values(&value) + .on_conflict(collections::uuid) + .do_update() + .set(&value) + .execute(conn) + .map_res("Error saving collection") + } + } } pub fn delete(self, conn: &DbConn) -> EmptyResult { @@ -70,9 +92,11 @@ impl Collection { CollectionCipher::delete_all_by_collection(&self.uuid, &conn)?; CollectionUser::delete_all_by_collection(&self.uuid, &conn)?; - diesel::delete(collections::table.filter(collections::uuid.eq(self.uuid))) - .execute(&**conn) - .map_res("Error deleting collection") + db_run! { conn: { + diesel::delete(collections::table.filter(collections::uuid.eq(self.uuid))) + .execute(conn) + .map_res("Error deleting collection") + }} } pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { @@ -91,33 +115,38 @@ impl Collection { } pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { - collections::table - .filter(collections::uuid.eq(uuid)) - .first::<Self>(&**conn) - .ok() + db_run! { conn: { + collections::table + .filter(collections::uuid.eq(uuid)) + .first::<CollectionDb>(conn) + .ok() + .from_db() + }} } pub fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec<Self> { - collections::table - .left_join(users_collections::table.on( - users_collections::collection_uuid.eq(collections::uuid).and( - users_collections::user_uuid.eq(user_uuid) - ) - )) - .left_join(users_organizations::table.on( - collections::org_uuid.eq(users_organizations::org_uuid).and( - users_organizations::user_uuid.eq(user_uuid) - ) - )) - .filter( - users_organizations::status.eq(UserOrgStatus::Confirmed as i32) - ) - .filter( - users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection - users_organizations::access_all.eq(true) // access_all in Organization + db_run! { conn: { + collections::table + .left_join(users_collections::table.on( + users_collections::collection_uuid.eq(collections::uuid).and( + users_collections::user_uuid.eq(user_uuid) + ) + )) + .left_join(users_organizations::table.on( + collections::org_uuid.eq(users_organizations::org_uuid).and( + users_organizations::user_uuid.eq(user_uuid) + ) + )) + .filter( + users_organizations::status.eq(UserOrgStatus::Confirmed as i32) ) - ).select(collections::all_columns) - .load::<Self>(&**conn).expect("Error loading collections") + .filter( + users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection + users_organizations::access_all.eq(true) // access_all in Organization + ) + ).select(collections::all_columns) + .load::<CollectionDb>(conn).expect("Error loading collections").from_db() + }} } pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec<Self> { @@ -128,42 +157,51 @@ impl Collection { } pub fn find_by_organization(org_uuid: &str, conn: &DbConn) -> Vec<Self> { - collections::table - .filter(collections::org_uuid.eq(org_uuid)) - .load::<Self>(&**conn) - .expect("Error loading collections") + db_run! { conn: { + collections::table + .filter(collections::org_uuid.eq(org_uuid)) + .load::<CollectionDb>(conn) + .expect("Error loading collections") + .from_db() + }} } pub fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option<Self> { - collections::table - .filter(collections::uuid.eq(uuid)) - .filter(collections::org_uuid.eq(org_uuid)) - .select(collections::all_columns) - .first::<Self>(&**conn) - .ok() + db_run! { conn: { + collections::table + .filter(collections::uuid.eq(uuid)) + .filter(collections::org_uuid.eq(org_uuid)) + .select(collections::all_columns) + .first::<CollectionDb>(conn) + .ok() + .from_db() + }} } pub fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> { - collections::table - .left_join(users_collections::table.on( - users_collections::collection_uuid.eq(collections::uuid).and( - users_collections::user_uuid.eq(user_uuid) - ) - )) - .left_join(users_organizations::table.on( - collections::org_uuid.eq(users_organizations::org_uuid).and( - users_organizations::user_uuid.eq(user_uuid) - ) - )) - .filter(collections::uuid.eq(uuid)) - .filter( - users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection - users_organizations::access_all.eq(true).or( // access_all in Organization - users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner + db_run! { conn: { + collections::table + .left_join(users_collections::table.on( + users_collections::collection_uuid.eq(collections::uuid).and( + users_collections::user_uuid.eq(user_uuid) ) - ) - ).select(collections::all_columns) - .first::<Self>(&**conn).ok() + )) + .left_join(users_organizations::table.on( + collections::org_uuid.eq(users_organizations::org_uuid).and( + users_organizations::user_uuid.eq(user_uuid) + ) + )) + .filter(collections::uuid.eq(uuid)) + .filter( + users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection + users_organizations::access_all.eq(true).or( // access_all in Organization + users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner + ) + ) + ).select(collections::all_columns) + .first::<CollectionDb>(conn).ok() + .from_db() + }} } pub fn is_writable_by_user(&self, user_uuid: &str, conn: &DbConn) -> bool { @@ -173,110 +211,108 @@ impl Collection { if user_org.access_all { true } else { - users_collections::table - .inner_join(collections::table) - .filter(users_collections::collection_uuid.eq(&self.uuid)) - .filter(users_collections::user_uuid.eq(&user_uuid)) - .filter(users_collections::read_only.eq(false)) - .select(collections::all_columns) - .first::<Self>(&**conn) - .ok() - .is_some() // Read only or no access to collection + db_run! { conn: { + users_collections::table + .inner_join(collections::table) + .filter(users_collections::collection_uuid.eq(&self.uuid)) + .filter(users_collections::user_uuid.eq(&user_uuid)) + .filter(users_collections::read_only.eq(false)) + .select(collections::all_columns) + .first::<CollectionDb>(conn) + .ok() + .is_some() // Read only or no access to collection + }} } } } } } -use super::User; - -#[derive(Debug, Identifiable, Queryable, Insertable, Associations)] -#[table_name = "users_collections"] -#[belongs_to(User, foreign_key = "user_uuid")] -#[belongs_to(Collection, foreign_key = "collection_uuid")] -#[primary_key(user_uuid, collection_uuid)] -pub struct CollectionUser { - pub user_uuid: String, - pub collection_uuid: String, - pub read_only: bool, - pub hide_passwords: bool, -} - /// Database methods impl CollectionUser { pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec<Self> { - users_collections::table - .filter(users_collections::user_uuid.eq(user_uuid)) - .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) - .filter(collections::org_uuid.eq(org_uuid)) - .select(users_collections::all_columns) - .load::<Self>(&**conn) - .expect("Error loading users_collections") - } - - #[cfg(feature = "postgresql")] - pub fn save(user_uuid: &str, collection_uuid: &str, read_only: bool, hide_passwords: bool, conn: &DbConn) -> EmptyResult { - User::update_uuid_revision(&user_uuid, conn); - - diesel::insert_into(users_collections::table) - .values(( - users_collections::user_uuid.eq(user_uuid), - users_collections::collection_uuid.eq(collection_uuid), - users_collections::read_only.eq(read_only), - users_collections::hide_passwords.eq(hide_passwords), - )) - .on_conflict((users_collections::user_uuid, users_collections::collection_uuid)) - .do_update() - .set(( - users_collections::read_only.eq(read_only), - users_collections::hide_passwords.eq(hide_passwords), - )) - .execute(&**conn) - .map_res("Error adding user to collection") + db_run! { conn: { + users_collections::table + .filter(users_collections::user_uuid.eq(user_uuid)) + .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) + .filter(collections::org_uuid.eq(org_uuid)) + .select(users_collections::all_columns) + .load::<CollectionUserDb>(conn) + .expect("Error loading users_collections") + .from_db() + }} } - #[cfg(not(feature = "postgresql"))] pub fn save(user_uuid: &str, collection_uuid: &str, read_only: bool, hide_passwords: bool, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&user_uuid, conn); - diesel::replace_into(users_collections::table) - .values(( - users_collections::user_uuid.eq(user_uuid), - users_collections::collection_uuid.eq(collection_uuid), - users_collections::read_only.eq(read_only), - users_collections::hide_passwords.eq(hide_passwords), - )) - .execute(&**conn) - .map_res("Error adding user to collection") + db_run! { conn: + sqlite, mysql { + diesel::replace_into(users_collections::table) + .values(( + users_collections::user_uuid.eq(user_uuid), + users_collections::collection_uuid.eq(collection_uuid), + users_collections::read_only.eq(read_only), + users_collections::hide_passwords.eq(hide_passwords), + )) + .execute(conn) + .map_res("Error adding user to collection") + } + postgresql { + diesel::insert_into(users_collections::table) + .values(( + users_collections::user_uuid.eq(user_uuid), + users_collections::collection_uuid.eq(collection_uuid), + users_collections::read_only.eq(read_only), + users_collections::hide_passwords.eq(hide_passwords), + )) + .on_conflict((users_collections::user_uuid, users_collections::collection_uuid)) + .do_update() + .set(( + users_collections::read_only.eq(read_only), + users_collections::hide_passwords.eq(hide_passwords), + )) + .execute(conn) + .map_res("Error adding user to collection") + } + } } pub fn delete(self, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn); - diesel::delete( - users_collections::table - .filter(users_collections::user_uuid.eq(&self.user_uuid)) - .filter(users_collections::collection_uuid.eq(&self.collection_uuid)), - ) - .execute(&**conn) - .map_res("Error removing user from collection") + db_run! { conn: { + diesel::delete( + users_collections::table + .filter(users_collections::user_uuid.eq(&self.user_uuid)) + .filter(users_collections::collection_uuid.eq(&self.collection_uuid)), + ) + .execute(conn) + .map_res("Error removing user from collection") + }} } pub fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec<Self> { - users_collections::table - .filter(users_collections::collection_uuid.eq(collection_uuid)) - .select(users_collections::all_columns) - .load::<Self>(&**conn) - .expect("Error loading users_collections") + db_run! { conn: { + users_collections::table + .filter(users_collections::collection_uuid.eq(collection_uuid)) + .select(users_collections::all_columns) + .load::<CollectionUserDb>(conn) + .expect("Error loading users_collections") + .from_db() + }} } pub fn find_by_collection_and_user(collection_uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> { - users_collections::table - .filter(users_collections::collection_uuid.eq(collection_uuid)) - .filter(users_collections::user_uuid.eq(user_uuid)) - .select(users_collections::all_columns) - .first::<Self>(&**conn) - .ok() + db_run! { conn: { + users_collections::table + .filter(users_collections::collection_uuid.eq(collection_uuid)) + .filter(users_collections::user_uuid.eq(user_uuid)) + .select(users_collections::all_columns) + .first::<CollectionUserDb>(conn) + .ok() + .from_db() + }} } pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { @@ -286,81 +322,81 @@ impl CollectionUser { User::update_uuid_revision(&collection.user_uuid, conn); }); - diesel::delete(users_collections::table.filter(users_collections::collection_uuid.eq(collection_uuid))) - .execute(&**conn) - .map_res("Error deleting users from collection") + db_run! { conn: { + diesel::delete(users_collections::table.filter(users_collections::collection_uuid.eq(collection_uuid))) + .execute(conn) + .map_res("Error deleting users from collection") + }} } pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&user_uuid, conn); - diesel::delete(users_collections::table.filter(users_collections::user_uuid.eq(user_uuid))) - .execute(&**conn) - .map_res("Error removing user from collections") + db_run! { conn: { + diesel::delete(users_collections::table.filter(users_collections::user_uuid.eq(user_uuid))) + .execute(conn) + .map_res("Error removing user from collections") + }} } } -use super::Cipher; - -#[derive(Debug, Identifiable, Queryable, Insertable, Associations)] -#[table_name = "ciphers_collections"] -#[belongs_to(Cipher, foreign_key = "cipher_uuid")] -#[belongs_to(Collection, foreign_key = "collection_uuid")] -#[primary_key(cipher_uuid, collection_uuid)] -pub struct CollectionCipher { - pub cipher_uuid: String, - pub collection_uuid: String, -} - /// Database methods impl CollectionCipher { - #[cfg(feature = "postgresql")] pub fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult { Self::update_users_revision(&collection_uuid, conn); - diesel::insert_into(ciphers_collections::table) - .values(( - ciphers_collections::cipher_uuid.eq(cipher_uuid), - ciphers_collections::collection_uuid.eq(collection_uuid), - )) - .on_conflict((ciphers_collections::cipher_uuid, ciphers_collections::collection_uuid)) - .do_nothing() - .execute(&**conn) - .map_res("Error adding cipher to collection") - } - #[cfg(not(feature = "postgresql"))] - pub fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult { - Self::update_users_revision(&collection_uuid, conn); - diesel::replace_into(ciphers_collections::table) - .values(( - ciphers_collections::cipher_uuid.eq(cipher_uuid), - ciphers_collections::collection_uuid.eq(collection_uuid), - )) - .execute(&**conn) - .map_res("Error adding cipher to collection") + db_run! { conn: + sqlite, mysql { + diesel::replace_into(ciphers_collections::table) + .values(( + ciphers_collections::cipher_uuid.eq(cipher_uuid), + ciphers_collections::collection_uuid.eq(collection_uuid), + )) + .execute(conn) + .map_res("Error adding cipher to collection") + } + postgresql { + diesel::insert_into(ciphers_collections::table) + .values(( + ciphers_collections::cipher_uuid.eq(cipher_uuid), + ciphers_collections::collection_uuid.eq(collection_uuid), + )) + .on_conflict((ciphers_collections::cipher_uuid, ciphers_collections::collection_uuid)) + .do_nothing() + .execute(conn) + .map_res("Error adding cipher to collection") + } + } } pub fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult { Self::update_users_revision(&collection_uuid, conn); - diesel::delete( - ciphers_collections::table - .filter(ciphers_collections::cipher_uuid.eq(cipher_uuid)) - .filter(ciphers_collections::collection_uuid.eq(collection_uuid)), - ) - .execute(&**conn) - .map_res("Error deleting cipher from collection") + + db_run! { conn: { + diesel::delete( + ciphers_collections::table + .filter(ciphers_collections::cipher_uuid.eq(cipher_uuid)) + .filter(ciphers_collections::collection_uuid.eq(collection_uuid)), + ) + .execute(conn) + .map_res("Error deleting cipher from collection") + }} } pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult { - diesel::delete(ciphers_collections::table.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))) - .execute(&**conn) - .map_res("Error removing cipher from collections") + db_run! { conn: { + diesel::delete(ciphers_collections::table.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))) + .execute(conn) + .map_res("Error removing cipher from collections") + }} } pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { - diesel::delete(ciphers_collections::table.filter(ciphers_collections::collection_uuid.eq(collection_uuid))) - .execute(&**conn) - .map_res("Error removing ciphers from collection") + db_run! { conn: { + diesel::delete(ciphers_collections::table.filter(ciphers_collections::collection_uuid.eq(collection_uuid))) + .execute(conn) + .map_res("Error removing ciphers from collection") + }} } pub fn update_users_revision(collection_uuid: &str, conn: &DbConn) { |