aboutsummaryrefslogtreecommitdiff
path: root/src/db/models/collection.rs
diff options
context:
space:
mode:
authorDaniel García <[email protected]>2020-08-18 17:15:44 +0200
committerDaniel García <[email protected]>2020-08-24 20:11:17 +0200
commit0365b7c6a4d8aa88fd9328fcc14beef300fe33a2 (patch)
treeb58aec0e0e57a422b95227538faeefb30f92e288 /src/db/models/collection.rs
parent19889187a5d3f48cbe5ad7ec3a0c4d0bcdb7b894 (diff)
downloadvaultwarden-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.rs468
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) {