diff --git a/Cargo.lock b/Cargo.lock index cf74b7f886..df9ad6f91d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -400,7 +400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b48814962d2fd604c50d2b9433c2a41a0ab567779ee2c02f7fba6eca1221f082" dependencies = [ "cached_proc_macro_types", - "darling", + "darling 0.14.4", "proc-macro2", "quote", "syn 1.0.109", @@ -597,8 +597,18 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core 0.20.3", + "darling_macro 0.20.3", ] [[package]] @@ -615,17 +625,42 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.28", +] + [[package]] name = "darling_macro" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core", + "darling_core 0.14.4", "quote", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core 0.20.3", + "quote", + "syn 2.0.28", +] + [[package]] name = "dashmap" version = "5.5.0" @@ -693,8 +728,7 @@ dependencies = [ [[package]] name = "diesel" version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7a532c1f99a0f596f6960a60d1e119e91582b24b39e2d83a190e61262c3ef0c" +source = "git+https://github.com/diesel-rs/diesel?rev=409585e9548f61f4e180ea5fe13ee4c3b7ab241c#409585e9548f61f4e180ea5fe13ee4c3b7ab241c" dependencies = [ "bitflags 2.4.0", "byteorder", @@ -713,10 +747,10 @@ dependencies = [ [[package]] name = "diesel_derives" version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74398b79d81e52e130d991afeed9c86034bb1b7735f46d2f5bf7deb261d80303" +source = "git+https://github.com/diesel-rs/diesel?rev=409585e9548f61f4e180ea5fe13ee4c3b7ab241c#409585e9548f61f4e180ea5fe13ee4c3b7ab241c" dependencies = [ "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2", "quote", "syn 2.0.28", @@ -735,8 +769,7 @@ dependencies = [ [[package]] name = "diesel_migrations" version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +source = "git+https://github.com/diesel-rs/diesel?rev=409585e9548f61f4e180ea5fe13ee4c3b7ab241c#409585e9548f61f4e180ea5fe13ee4c3b7ab241c" dependencies = [ "diesel", "migrations_internals", @@ -746,8 +779,7 @@ dependencies = [ [[package]] name = "diesel_table_macro_syntax" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +source = "git+https://github.com/diesel-rs/diesel?rev=409585e9548f61f4e180ea5fe13ee4c3b7ab241c#409585e9548f61f4e180ea5fe13ee4c3b7ab241c" dependencies = [ "syn 2.0.28", ] @@ -769,6 +801,19 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dsl_auto_type" +version = "0.1.0" +source = "git+https://github.com/diesel-rs/diesel?rev=409585e9548f61f4e180ea5fe13ee4c3b7ab241c#409585e9548f61f4e180ea5fe13ee4c3b7ab241c" +dependencies = [ + "darling 0.20.3", + "either", + "heck", + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "either" version = "1.9.0" @@ -1645,8 +1690,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "migrations_internals" version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +source = "git+https://github.com/diesel-rs/diesel?rev=409585e9548f61f4e180ea5fe13ee4c3b7ab241c#409585e9548f61f4e180ea5fe13ee4c3b7ab241c" dependencies = [ "serde", "toml", @@ -1655,8 +1699,7 @@ dependencies = [ [[package]] name = "migrations_macros" version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +source = "git+https://github.com/diesel-rs/diesel?rev=409585e9548f61f4e180ea5fe13ee4c3b7ab241c#409585e9548f61f4e180ea5fe13ee4c3b7ab241c" dependencies = [ "migrations_internals", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 6875db8ab9..b8955b92e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,11 @@ publish = false build = "build.rs" [features] -# default = ["sqlite"] +default = [ + # "sqlite", + # "postgresql", + # "mysql", +] # Empty to keep compatibility, prefer to set USE_SYSLOG=true enable_syslog = [] mysql = ["diesel/mysql", "diesel_migrations/mysql"] @@ -166,6 +170,8 @@ rpassword = "7.2.0" [patch.crates-io] rocket = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'ce441b5f46fdf5cd99cb32b8b8638835e4c2a5fa' } # v0.5 branch # rocket_ws = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'ce441b5f46fdf5cd99cb32b8b8638835e4c2a5fa' } # v0.5 branch +diesel = { git = 'https://github.com/diesel-rs/diesel', rev = "409585e9548f61f4e180ea5fe13ee4c3b7ab241c" } +diesel_migrations = { git = 'https://github.com/diesel-rs/diesel', rev = "409585e9548f61f4e180ea5fe13ee4c3b7ab241c" } # Strip debuginfo from the release builds # Also enable thin LTO for some optimizations diff --git a/src/api/admin.rs b/src/api/admin.rs index 9eb965cc86..41f2885e1d 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -70,15 +70,22 @@ pub fn catchers() -> Vec { static DB_TYPE: Lazy<&str> = Lazy::new(|| { DbConnType::from_url(&CONFIG.database_url()) .map(|t| match t { - DbConnType::sqlite => "SQLite", - DbConnType::mysql => "MySQL", - DbConnType::postgresql => "PostgreSQL", + #[cfg(sqlite)] + DbConnType::Sqlite => "SQLite", + #[cfg(mysql)] + DbConnType::Mysql => "MySQL", + #[cfg(postgresql)] + DbConnType::Postgresql => "PostgreSQL", }) .unwrap_or("Unknown") }); +#[cfg(sqlite)] static CAN_BACKUP: Lazy = - Lazy::new(|| DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::sqlite).unwrap_or(false)); + Lazy::new(|| DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::Sqlite).unwrap_or(false)); + +#[cfg(not(sqlite))] +static CAN_BACKUP: Lazy = Lazy::new(|| false); #[get("/")] fn admin_disabled() -> &'static str { diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs index 1ea790e638..cf6b60ffc7 100644 --- a/src/api/core/sends.rs +++ b/src/api/core/sends.rs @@ -340,9 +340,13 @@ async fn post_send_file_v2_data( let mut data = data.into_inner(); - let Some(send) = Send::find_by_uuid(send_uuid, &mut conn).await else { err!("Send not found. Unable to save the file.") }; + let Some(send) = Send::find_by_uuid(send_uuid, &mut conn).await else { + err!("Send not found. Unable to save the file.") + }; - let Some(send_user_id) = &send.user_uuid else {err!("Sends are only supported for users at the moment")}; + let Some(send_user_id) = &send.user_uuid else { + err!("Sends are only supported for users at the moment") + }; if send_user_id != &headers.user.uuid { err!("Send doesn't belong to user"); } diff --git a/src/api/icons.rs b/src/api/icons.rs index 124f3a8f78..5e2c1dc372 100644 --- a/src/api/icons.rs +++ b/src/api/icons.rs @@ -685,7 +685,9 @@ async fn download_icon(domain: &str) -> Result<(Bytes, Option<&str>), Error> { for icon in icon_result.iconlist.iter().take(5) { if icon.href.starts_with("data:image") { - let Ok(datauri) = DataUrl::process(&icon.href) else {continue}; + let Ok(datauri) = DataUrl::process(&icon.href) else { + continue; + }; // Check if we are able to decode the data uri let mut body = BytesMut::new(); match datauri.decode::<_, ()>(|bytes| { diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 7e76021b35..a4ef43bf1d 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -115,8 +115,12 @@ fn websockets_hub<'r>( let addr = ip.ip; info!("Accepting Rocket WS connection from {addr}"); - let Some(token) = data.access_token else { err_code!("Invalid claim", 401) }; - let Ok(claims) = crate::auth::decode_login(&token) else { err_code!("Invalid token", 401) }; + let Some(token) = data.access_token else { + err_code!("Invalid claim", 401) + }; + let Ok(claims) = crate::auth::decode_login(&token) else { + err_code!("Invalid token", 401) + }; let (mut rx, guard) = { let users = Arc::clone(&WS_USERS); diff --git a/src/api/web.rs b/src/api/web.rs index 5cdcb15e52..5fbc56d222 100644 --- a/src/api/web.rs +++ b/src/api/web.rs @@ -94,7 +94,9 @@ async fn web_files(p: PathBuf) -> Cached> { #[get("/attachments//?")] async fn attachments(uuid: SafeString, file_id: SafeString, token: String) -> Option { - let Ok(claims) = decode_file_download(&token) else { return None }; + let Ok(claims) = decode_file_download(&token) else { + return None; + }; if claims.sub != *uuid || claims.file_id != *file_id { return None; } diff --git a/src/config.rs b/src/config.rs index d54b356b15..d4c18b14cf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,7 +7,6 @@ use once_cell::sync::Lazy; use reqwest::Url; use crate::{ - db::DbConnType, error::Error, util::{get_env, get_env_bool}, }; @@ -687,12 +686,18 @@ make_config! { fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { // Validate connection URL is valid and DB feature is enabled - let url = &cfg.database_url; - if DbConnType::from_url(url)? == DbConnType::sqlite && url.contains('/') { - let path = std::path::Path::new(&url); - if let Some(parent) = path.parent() { - if !parent.is_dir() { - err!(format!("SQLite database directory `{}` does not exist or is not a directory", parent.display())); + #[cfg(sqlite)] + { + let url = &cfg.database_url; + if crate::db::DbConnType::from_url(url)? == crate::db::DbConnType::Sqlite && url.contains('/') { + let path = std::path::Path::new(&url); + if let Some(parent) = path.parent() { + if !parent.is_dir() { + err!(format!( + "SQLite database directory `{}` does not exist or is not a directory", + parent.display() + )); + } } } } diff --git a/src/db/mod.rs b/src/db/mod.rs index d3357182f7..9d74ae8b32 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -22,20 +22,7 @@ use crate::{ CONFIG, }; -#[cfg(sqlite)] -#[path = "schemas/sqlite/schema.rs"] -pub mod __sqlite_schema; - -#[cfg(mysql)] -#[path = "schemas/mysql/schema.rs"] -pub mod __mysql_schema; - -#[cfg(postgresql)] -#[path = "schemas/postgresql/schema.rs"] -pub mod __postgresql_schema; - // These changes are based on Rocket 0.5-rc wrapper of Diesel: https://github.com/SergioBenitez/Rocket/blob/v0.5-rc/contrib/sync_db_pools - // A wrapper around spawn_blocking that propagates panics to the calling code. pub async fn run_blocking(job: F) -> R where @@ -51,171 +38,166 @@ where } } -// This is used to generate the main DbConn and DbPool enums, which contain one variant for each database supported -macro_rules! generate_connections { - ( $( $name:ident: $ty:ty ),+ ) => { - #[allow(non_camel_case_types, dead_code)] - #[derive(Eq, PartialEq)] - pub enum DbConnType { $( $name, )+ } - - pub struct DbConn { - conn: Arc>>, - permit: Option, - } +#[derive(diesel::MultiConnection)] +pub enum DbConnInner { + #[cfg(sqlite)] + Sqlite(diesel::sqlite::SqliteConnection), + #[cfg(mysql)] + Mysql(diesel::mysql::MysqlConnection), + #[cfg(postgresql)] + Postgresql(diesel::pg::PgConnection), +} - #[allow(non_camel_case_types)] - pub enum DbConnInner { $( #[cfg($name)] $name(PooledConnection>), )+ } +#[derive(Eq, PartialEq)] +pub enum DbConnType { + #[cfg(sqlite)] + Sqlite, + #[cfg(mysql)] + Mysql, + #[cfg(postgresql)] + Postgresql, +} - #[derive(Debug)] - pub struct DbConnOptions { - pub init_stmts: String, - } +pub struct DbConn { + conn: Arc>>>>, + permit: Option, +} - $( // Based on . - #[cfg($name)] - impl CustomizeConnection<$ty, diesel::r2d2::Error> for DbConnOptions { - fn on_acquire(&self, conn: &mut $ty) -> Result<(), diesel::r2d2::Error> { - if !self.init_stmts.is_empty() { - conn.batch_execute(&self.init_stmts).map_err(diesel::r2d2::Error::QueryError)?; - } - Ok(()) - } - })+ +#[derive(Debug)] +pub struct DbConnOptions { + pub init_stmts: String, +} - #[derive(Clone)] - pub struct DbPool { - // This is an 'Option' so that we can drop the pool in a 'spawn_blocking'. - pool: Option, - semaphore: Arc +impl CustomizeConnection for DbConnOptions { + fn on_acquire(&self, conn: &mut DbConnInner) -> Result<(), diesel::r2d2::Error> { + if !self.init_stmts.is_empty() { + conn.batch_execute(&self.init_stmts).map_err(diesel::r2d2::Error::QueryError)?; } + Ok(()) + } +} - #[allow(non_camel_case_types)] - #[derive(Clone)] - pub enum DbPoolInner { $( #[cfg($name)] $name(Pool>), )+ } - - impl Drop for DbConn { - fn drop(&mut self) { - let conn = Arc::clone(&self.conn); - let permit = self.permit.take(); - - // Since connection can't be on the stack in an async fn during an - // await, we have to spawn a new blocking-safe thread... - tokio::task::spawn_blocking(move || { - // And then re-enter the runtime to wait on the async mutex, but in a blocking fashion. - let mut conn = tokio::runtime::Handle::current().block_on(conn.lock_owned()); - - if let Some(conn) = conn.take() { - drop(conn); - } +#[derive(Clone)] +pub struct DbPool { + pool: Option>>, + semaphore: Arc, +} - // Drop permit after the connection is dropped - drop(permit); - }); +impl Drop for DbConn { + fn drop(&mut self) { + let conn = Arc::clone(&self.conn); + let permit = self.permit.take(); + tokio::task::spawn_blocking(move || { + let mut conn = tokio::runtime::Handle::current().block_on(conn.lock_owned()); + if let Some(conn) = conn.take() { + drop(conn); } - } + drop(permit); + }); + } +} - impl Drop for DbPool { - fn drop(&mut self) { - let pool = self.pool.take(); - tokio::task::spawn_blocking(move || drop(pool)); - } +impl Drop for DbPool { + fn drop(&mut self) { + let pool = self.pool.take(); + // Only use spawn_blocking if the Tokio runtime is still available + if let Ok(handle) = tokio::runtime::Handle::try_current() { + handle.spawn_blocking(move || drop(pool)); } + // Otherwise the pool will be dropped on the current thread + } +} - impl DbPool { - // For the given database URL, guess its type, run migrations, create pool, and return it - pub fn from_config() -> Result { - let url = CONFIG.database_url(); - let conn_type = DbConnType::from_url(&url)?; - - match conn_type { $( - DbConnType::$name => { - #[cfg($name)] - { - paste::paste!{ [< $name _migrations >]::run_migrations()?; } - let manager = ConnectionManager::new(&url); - let pool = Pool::builder() - .max_size(CONFIG.database_max_conns()) - .connection_timeout(Duration::from_secs(CONFIG.database_timeout())) - .connection_customizer(Box::new(DbConnOptions{ - init_stmts: conn_type.get_init_stmts() - })) - .build(manager) - .map_res("Failed to create pool")?; - Ok(DbPool { - pool: Some(DbPoolInner::$name(pool)), - semaphore: Arc::new(Semaphore::new(CONFIG.database_max_conns() as usize)), - }) - } - #[cfg(not($name))] - unreachable!("Trying to use a DB backend when it's feature is disabled") - }, - )+ } +impl DbPool { + pub fn from_config() -> Result { + let url = CONFIG.database_url(); + let conn_type = DbConnType::from_url(&url)?; + match conn_type { + #[cfg(sqlite)] + DbConnType::Sqlite => { + #[cfg(feature = "sqlite")] + { + sqlite_migrations::run_migrations(&url)?; + } + } + #[cfg(mysql)] + DbConnType::Mysql => { + #[cfg(feature = "mysql")] + { + mysql_migrations::run_migrations(&url)?; + } } - // Get a connection from the pool - pub async fn get(&self) -> Result { - let duration = Duration::from_secs(CONFIG.database_timeout()); - let permit = match timeout(duration, Arc::clone(&self.semaphore).acquire_owned()).await { - Ok(p) => p.expect("Semaphore should be open"), - Err(_) => { - err!("Timeout waiting for database connection"); - } - }; - - match self.pool.as_ref().expect("DbPool.pool should always be Some()") { $( - #[cfg($name)] - DbPoolInner::$name(p) => { - let pool = p.clone(); - let c = run_blocking(move || pool.get_timeout(duration)).await.map_res("Error retrieving connection from pool")?; - - Ok(DbConn { - conn: Arc::new(Mutex::new(Some(DbConnInner::$name(c)))), - permit: Some(permit) - }) - }, - )+ } + #[cfg(postgresql)] + DbConnType::Postgresql => { + #[cfg(feature = "postgresql")] + { + postgresql_migrations::run_migrations(&url)?; + } } } - }; -} -#[cfg(not(query_logger))] -generate_connections! { - sqlite: diesel::sqlite::SqliteConnection, - mysql: diesel::mysql::MysqlConnection, - postgresql: diesel::pg::PgConnection -} + let max_conns = CONFIG.database_max_conns(); + let manager = ConnectionManager::::new(&url); + let pool = Pool::builder() + .max_size(max_conns) + .connection_timeout(Duration::from_secs(CONFIG.database_timeout())) + .connection_customizer(Box::new(DbConnOptions { + init_stmts: conn_type.get_init_stmts(), + })) + .build(manager) + .map_res("Failed to create pool")?; + + Ok(DbPool { + pool: Some(pool), + semaphore: Arc::new(Semaphore::new(max_conns as usize)), + }) + } -#[cfg(query_logger)] -generate_connections! { - sqlite: diesel_logger::LoggingConnection, - mysql: diesel_logger::LoggingConnection, - postgresql: diesel_logger::LoggingConnection + pub async fn get(&self) -> Result { + let duration = Duration::from_secs(CONFIG.database_timeout()); + let permit = match timeout(duration, Arc::clone(&self.semaphore).acquire_owned()).await { + Ok(p) => p.expect("Semaphore should be open"), + Err(_) => { + err!("Timeout waiting for database connection"); + } + }; + + let p = self.pool.as_ref().expect("DbPool.pool should always be Some()"); + let pool = p.clone(); + let c = + run_blocking(move || pool.get_timeout(duration)).await.map_res("Error retrieving connection from pool")?; + Ok(DbConn { + conn: Arc::new(Mutex::new(Some(c))), + permit: Some(permit), + }) + } } impl DbConnType { - pub fn from_url(url: &str) -> Result { + // pub enum Backend + pub fn from_url(url: &str) -> Result { // Mysql - if url.starts_with("mysql:") { - #[cfg(mysql)] - return Ok(DbConnType::mysql); + if url.len() > 6 && &url[..6] == "mysql:" { + #[cfg(feature = "mysql")] + return Ok(DbConnType::Mysql); - #[cfg(not(mysql))] + #[cfg(not(feature = "mysql"))] err!("`DATABASE_URL` is a MySQL URL, but the 'mysql' feature is not enabled") - // Postgres - } else if url.starts_with("postgresql:") || url.starts_with("postgres:") { - #[cfg(postgresql)] - return Ok(DbConnType::postgresql); + // Postgresql + } else if url.len() > 11 && (&url[..11] == "postgresql:" || &url[..9] == "postgres:") { + #[cfg(feature = "postgresql")] + return Ok(DbConnType::Postgresql); - #[cfg(not(postgresql))] + #[cfg(not(feature = "postgresql"))] err!("`DATABASE_URL` is a PostgreSQL URL, but the 'postgresql' feature is not enabled") //Sqlite } else { - #[cfg(sqlite)] - return Ok(DbConnType::sqlite); + #[cfg(feature = "sqlite")] + return Ok(DbConnType::Sqlite); - #[cfg(not(sqlite))] + #[cfg(not(feature = "sqlite"))] err!("`DATABASE_URL` looks like a SQLite URL, but 'sqlite' feature is not enabled") } } @@ -231,148 +213,61 @@ impl DbConnType { pub fn default_init_stmts(&self) -> String { match self { - Self::sqlite => "PRAGMA busy_timeout = 5000; PRAGMA synchronous = NORMAL;".to_string(), - Self::mysql => String::new(), - Self::postgresql => String::new(), + #[cfg(sqlite)] + Self::Sqlite => "PRAGMA busy_timeout = 5000; PRAGMA synchronous = NORMAL;".to_string(), + #[cfg(mysql)] + Self::Mysql => String::new(), + #[cfg(postgresql)] + Self::Postgresql => String::new(), } } } -#[macro_export] -macro_rules! db_run { - // Same for all dbs - ( $conn:ident: $body:block ) => { - db_run! { $conn: sqlite, mysql, postgresql $body } - }; - - ( @raw $conn:ident: $body:block ) => { - db_run! { @raw $conn: sqlite, mysql, postgresql $body } - }; - - // Different code for each db - ( $conn:ident: $( $($db:ident),+ $body:block )+ ) => {{ - #[allow(unused)] use diesel::prelude::*; - #[allow(unused)] use $crate::db::FromDb; - - let conn = $conn.conn.clone(); - let mut conn = conn.lock_owned().await; - match conn.as_mut().expect("internal invariant broken: self.connection is Some") { - $($( - #[cfg($db)] - $crate::db::DbConnInner::$db($conn) => { - paste::paste! { - #[allow(unused)] use $crate::db::[<__ $db _schema>]::{self as schema, *}; - #[allow(unused)] use [<__ $db _model>]::*; - } - - tokio::task::block_in_place(move || { $body }) // Run blocking can't be used due to the 'static limitation, use block_in_place instead - }, - )+)+ - } - }}; - - ( @raw $conn:ident: $( $($db:ident),+ $body:block )+ ) => {{ - #[allow(unused)] use diesel::prelude::*; - #[allow(unused)] use $crate::db::FromDb; +// Shared base code for the db_run macro. +macro_rules! db_run_base { + ( $conn:ident ) => { + #[allow(unused)] + use diesel::prelude::*; + #[allow(unused)] + use $crate::db::models::{self, *}; + #[allow(unused)] + use $crate::db::schema::{self, *}; let conn = $conn.conn.clone(); let mut conn = conn.lock_owned().await; - match conn.as_mut().expect("internal invariant broken: self.connection is Some") { - $($( - #[cfg($db)] - $crate::db::DbConnInner::$db($conn) => { - paste::paste! { - #[allow(unused)] use $crate::db::[<__ $db _schema>]::{self as schema, *}; - // @ RAW: #[allow(unused)] use [<__ $db _model>]::*; - } - - tokio::task::block_in_place(move || { $body }) // Run blocking can't be used due to the 'static limitation, use block_in_place instead - }, - )+)+ - } - }}; -} - -pub trait FromDb { - type Output; - #[allow(clippy::wrong_self_convention)] - fn from_db(self) -> Self::Output; -} - -impl FromDb for Vec { - type Output = Vec; - #[allow(clippy::wrong_self_convention)] - #[inline(always)] - fn from_db(self) -> Self::Output { - self.into_iter().map(crate::db::FromDb::from_db).collect() - } -} - -impl FromDb for Option { - type Output = Option; - #[allow(clippy::wrong_self_convention)] - #[inline(always)] - fn from_db(self) -> Self::Output { - self.map(crate::db::FromDb::from_db) - } + let $conn = conn.as_mut().expect("internal invariant broken: self.conn is Some"); + }; } -// For each struct eg. Cipher, we create a CipherDb inside a module named __$db_model (where $db is sqlite, mysql or postgresql), -// to implement the Diesel traits. We also provide methods to convert between them and the basic structs. Later, that module will be auto imported when using db_run! #[macro_export] -macro_rules! db_object { - ( $( - $( #[$attr:meta] )* - pub struct $name:ident { - $( $( #[$field_attr:meta] )* $vis:vis $field:ident : $typ:ty ),+ - $(,)? - } - )+ ) => { - // Create the normal struct, without attributes - $( pub struct $name { $( /*$( #[$field_attr] )**/ $vis $field : $typ, )+ } )+ - - #[cfg(sqlite)] - pub mod __sqlite_model { $( db_object! { @db sqlite | $( #[$attr] )* | $name | $( $( #[$field_attr] )* $field : $typ ),+ } )+ } - #[cfg(mysql)] - pub mod __mysql_model { $( db_object! { @db mysql | $( #[$attr] )* | $name | $( $( #[$field_attr] )* $field : $typ ),+ } )+ } - #[cfg(postgresql)] - pub mod __postgresql_model { $( db_object! { @db postgresql | $( #[$attr] )* | $name | $( $( #[$field_attr] )* $field : $typ ),+ } )+ } - }; - - ( @db $db:ident | $( #[$attr:meta] )* | $name:ident | $( $( #[$field_attr:meta] )* $vis:vis $field:ident : $typ:ty),+) => { - paste::paste! { - #[allow(unused)] use super::*; - #[allow(unused)] use diesel::prelude::*; - #[allow(unused)] use $crate::db::[<__ $db _schema>]::*; - - $( #[$attr] )* - pub struct [<$name Db>] { $( - $( #[$field_attr] )* $vis $field : $typ, - )+ } - - impl [<$name Db>] { - #[allow(clippy::wrong_self_convention)] - #[inline(always)] pub fn to_db(x: &super::$name) -> Self { Self { $( $field: x.$field.clone(), )+ } } - } +macro_rules! db_run { + ( $conn:ident: $body:block ) => {{ + db_run_base!($conn); + tokio::task::block_in_place(move || $body ) // Run blocking can't be used due to the 'static limitation, use block_in_place instead + }}; - impl $crate::db::FromDb for [<$name Db>] { - type Output = super::$name; - #[allow(clippy::wrong_self_convention)] - #[inline(always)] fn from_db(self) -> Self::Output { super::$name { $( $field: self.$field, )+ } } - } - } - }; + ( $conn:ident: $( $($db:ident),+ $body:block )+ ) => {{ + db_run_base!($conn); + match std::ops::DerefMut::deref_mut($conn) { + $($( + #[cfg($db)] + paste::paste!($crate::db::DbConnInner::[<$db:camel>](ref mut $conn)) => { + tokio::task::block_in_place(move || $body ) // Run blocking can't be used due to the 'static limitation, use block_in_place instead + }, + )+)+} + }}; } +#[path = "schemas/schema.rs"] +pub mod schema; + // Reexport the models, needs to be after the macros are defined so it can access them pub mod models; -/// Creates a back-up of the sqlite database -/// MySQL/MariaDB and PostgreSQL are not supported. +#[allow(unused_variables)] // Since we do not use `conn` in PostgreSQL and MySQL pub async fn backup_database(conn: &mut DbConn) -> Result<(), Error> { - db_run! {@raw conn: + db_run! {conn: postgresql, mysql { - let _ = conn; err!("PostgreSQL and MySQL/MariaDB do not support this backup feature"); } sqlite { @@ -388,7 +283,7 @@ pub async fn backup_database(conn: &mut DbConn) -> Result<(), Error> { /// Get the SQL Server version pub async fn get_sql_server_version(conn: &mut DbConn) -> String { - db_run! {@raw conn: + db_run! {conn: postgresql, mysql { sql_function!{ fn version() -> diesel::sql_types::Text; @@ -427,13 +322,11 @@ mod sqlite_migrations { use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/sqlite"); - pub fn run_migrations() -> Result<(), super::Error> { + pub fn run_migrations(url: &str) -> Result<(), super::Error> { use diesel::{Connection, RunQueryDsl}; - let url = crate::CONFIG.database_url(); - // Establish a connection to the sqlite database (this will create a new one, if it does // not exist, and exit if there is an error). - let mut connection = diesel::sqlite::SqliteConnection::establish(&url)?; + let mut connection = diesel::sqlite::SqliteConnection::establish(url)?; // Run the migrations after successfully establishing a connection // Disable Foreign Key Checks during migration @@ -457,10 +350,10 @@ mod mysql_migrations { use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/mysql"); - pub fn run_migrations() -> Result<(), super::Error> { + pub fn run_migrations(url: &str) -> Result<(), super::Error> { use diesel::{Connection, RunQueryDsl}; // Make sure the database is up to date (create if it doesn't exist, or run the migrations) - let mut connection = diesel::mysql::MysqlConnection::establish(&crate::CONFIG.database_url())?; + let mut connection = diesel::mysql::MysqlConnection::establish(url)?; // Disable Foreign Key Checks during migration // Scoped to a connection/session. @@ -478,10 +371,10 @@ mod postgresql_migrations { use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/postgresql"); - pub fn run_migrations() -> Result<(), super::Error> { + pub fn run_migrations(url: &str) -> Result<(), super::Error> { use diesel::{Connection, RunQueryDsl}; // Make sure the database is up to date (create if it doesn't exist, or run the migrations) - let mut connection = diesel::pg::PgConnection::establish(&crate::CONFIG.database_url())?; + let mut connection = diesel::pg::PgConnection::establish(url)?; // Disable Foreign Key Checks during migration // FIXME: Per https://www.postgresql.org/docs/12/sql-set-constraints.html, diff --git a/src/db/models/attachment.rs b/src/db/models/attachment.rs index 616aae2f34..9fd0e9c7a5 100644 --- a/src/db/models/attachment.rs +++ b/src/db/models/attachment.rs @@ -2,20 +2,19 @@ use std::io::ErrorKind; use serde_json::Value; +use crate::db::schema::attachments; use crate::CONFIG; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = attachments)] - #[diesel(treat_none_as_null = true)] - #[diesel(primary_key(id))] - pub struct Attachment { - pub id: String, - pub cipher_uuid: String, - pub file_name: String, // encrypted - pub file_size: i32, - pub akey: Option, - } +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = attachments)] +#[diesel(treat_none_as_null = true)] +#[diesel(primary_key(id))] +pub struct Attachment { + pub id: String, + pub cipher_uuid: String, + pub file_name: String, // encrypted + pub file_size: i32, + pub akey: Option, } /// Local methods @@ -64,7 +63,7 @@ impl Attachment { db_run! { conn: sqlite, mysql { match diesel::replace_into(attachments::table) - .values(AttachmentDb::to_db(self)) + .values(self) .execute(conn) { Ok(_) => Ok(()), @@ -72,7 +71,7 @@ impl Attachment { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(attachments::table) .filter(attachments::id.eq(&self.id)) - .set(AttachmentDb::to_db(self)) + .set(self) .execute(conn) .map_res("Error saving attachment") } @@ -80,12 +79,11 @@ impl Attachment { }.map_res("Error saving attachment") } postgresql { - let value = AttachmentDb::to_db(self); diesel::insert_into(attachments::table) - .values(&value) + .values(self) .on_conflict(attachments::id) .do_update() - .set(&value) + .set(self) .execute(conn) .map_res("Error saving attachment") } @@ -127,9 +125,8 @@ impl Attachment { db_run! { conn: { attachments::table .filter(attachments::id.eq(id.to_lowercase())) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -137,9 +134,8 @@ impl Attachment { db_run! { conn: { attachments::table .filter(attachments::cipher_uuid.eq(cipher_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading attachments") - .from_db() }} } @@ -199,9 +195,8 @@ impl Attachment { .filter(ciphers::user_uuid.eq(user_uuid)) .or_filter(ciphers::organization_uuid.eq_any(org_uuids)) .select(attachments::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading attachments") - .from_db() }} } } diff --git a/src/db/models/auth_request.rs b/src/db/models/auth_request.rs index 0b129ac16a..d360b73c2f 100644 --- a/src/db/models/auth_request.rs +++ b/src/db/models/auth_request.rs @@ -1,34 +1,33 @@ use crate::crypto::ct_eq; +use crate::db::schema::auth_requests; use chrono::{NaiveDateTime, Utc}; -db_object! { - #[derive(Debug, Identifiable, Queryable, Insertable, AsChangeset, Deserialize, Serialize)] - #[diesel(table_name = auth_requests)] - #[diesel(treat_none_as_null = true)] - #[diesel(primary_key(uuid))] - pub struct AuthRequest { - pub uuid: String, - pub user_uuid: String, - pub organization_uuid: Option, +#[derive(Debug, Identifiable, Queryable, Insertable, AsChangeset, Deserialize, Serialize)] +#[diesel(table_name = auth_requests)] +#[diesel(treat_none_as_null = true)] +#[diesel(primary_key(uuid))] +pub struct AuthRequest { + pub uuid: String, + pub user_uuid: String, + pub organization_uuid: Option, - pub request_device_identifier: String, - pub device_type: i32, // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs + pub request_device_identifier: String, + pub device_type: i32, // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs - pub request_ip: String, - pub response_device_id: Option, + pub request_ip: String, + pub response_device_id: Option, - pub access_code: String, - pub public_key: String, + pub access_code: String, + pub public_key: String, - pub enc_key: String, + pub enc_key: String, - pub master_password_hash: String, - pub approved: Option, - pub creation_date: NaiveDateTime, - pub response_date: Option, + pub master_password_hash: String, + pub approved: Option, + pub creation_date: NaiveDateTime, + pub response_date: Option, - pub authentication_date: Option, - } + pub authentication_date: Option, } impl AuthRequest { @@ -73,7 +72,7 @@ impl AuthRequest { db_run! { conn: sqlite, mysql { match diesel::replace_into(auth_requests::table) - .values(AuthRequestDb::to_db(self)) + .values(&*self) .execute(conn) { Ok(_) => Ok(()), @@ -81,7 +80,7 @@ impl AuthRequest { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(auth_requests::table) .filter(auth_requests::uuid.eq(&self.uuid)) - .set(AuthRequestDb::to_db(self)) + .set(&*self) .execute(conn) .map_res("Error auth_request") } @@ -89,12 +88,11 @@ impl AuthRequest { }.map_res("Error auth_request") } postgresql { - let value = AuthRequestDb::to_db(self); diesel::insert_into(auth_requests::table) - .values(&value) + .values(&*self) .on_conflict(auth_requests::uuid) .do_update() - .set(&value) + .set(&*self) .execute(conn) .map_res("Error saving auth_request") } @@ -105,9 +103,8 @@ impl AuthRequest { db_run! {conn: { auth_requests::table .filter(auth_requests::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -115,7 +112,7 @@ impl AuthRequest { db_run! {conn: { auth_requests::table .filter(auth_requests::user_uuid.eq(user_uuid)) - .load::(conn).expect("Error loading auth_requests").from_db() + .load::(conn).expect("Error loading auth_requests") }} } @@ -123,7 +120,7 @@ impl AuthRequest { db_run! {conn: { auth_requests::table .filter(auth_requests::creation_date.lt(dt)) - .load::(conn).expect("Error loading auth_requests").from_db() + .load::(conn).expect("Error loading auth_requests") }} } diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index f76490b4ab..0a1a806c80 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -2,45 +2,42 @@ use crate::CONFIG; use chrono::{Duration, NaiveDateTime, Utc}; use serde_json::Value; -use super::{ - Attachment, CollectionCipher, Favorite, FolderCipher, Group, User, UserOrgStatus, UserOrgType, UserOrganization, -}; +use super::{Attachment, CollectionCipher, Favorite, FolderCipher, Group, User, UserOrganization}; use crate::api::core::{CipherData, CipherSyncData, CipherSyncType}; +use crate::db::schema::ciphers; use std::borrow::Cow; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = ciphers)] - #[diesel(treat_none_as_null = true)] - #[diesel(primary_key(uuid))] - pub struct Cipher { - pub uuid: String, - pub created_at: NaiveDateTime, - pub updated_at: NaiveDateTime, - - pub user_uuid: Option, - pub organization_uuid: Option, - - /* - Login = 1, - SecureNote = 2, - Card = 3, - Identity = 4, - Fido2key = 5 - */ - pub atype: i32, - pub name: String, - pub notes: Option, - pub fields: Option, - - pub data: String, - - pub password_history: Option, - pub deleted_at: Option, - pub reprompt: Option, - } +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = ciphers)] +#[diesel(treat_none_as_null = true)] +#[diesel(primary_key(uuid))] +pub struct Cipher { + pub uuid: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + + pub user_uuid: Option, + pub organization_uuid: Option, + + /* + Login = 1, + SecureNote = 2, + Card = 3, + Identity = 4, + Fido2key = 5 + */ + pub atype: i32, + pub name: String, + pub notes: Option, + pub fields: Option, + + pub data: String, + + pub password_history: Option, + pub deleted_at: Option, + pub reprompt: Option, } #[allow(dead_code)] @@ -288,7 +285,7 @@ impl Cipher { db_run! { conn: sqlite, mysql { match diesel::replace_into(ciphers::table) - .values(CipherDb::to_db(self)) + .values(&*self) .execute(conn) { Ok(_) => Ok(()), @@ -296,7 +293,7 @@ impl Cipher { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(ciphers::table) .filter(ciphers::uuid.eq(&self.uuid)) - .set(CipherDb::to_db(self)) + .set(&*self) .execute(conn) .map_res("Error saving cipher") } @@ -304,12 +301,11 @@ impl Cipher { }.map_res("Error saving cipher") } postgresql { - let value = CipherDb::to_db(self); diesel::insert_into(ciphers::table) - .values(&value) + .values(&*self) .on_conflict(ciphers::uuid) .do_update() - .set(&value) + .set(&*self) .execute(conn) .map_res("Error saving cipher") } @@ -572,9 +568,8 @@ impl Cipher { db_run! {conn: { ciphers::table .filter(ciphers::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -633,7 +628,7 @@ impl Cipher { query .select(ciphers::all_columns) .distinct() - .load::(conn).expect("Error loading ciphers").from_db() + .load::(conn).expect("Error loading ciphers") }} } @@ -650,7 +645,7 @@ impl Cipher { ciphers::user_uuid.eq(user_uuid) .and(ciphers::organization_uuid.is_null()) ) - .load::(conn).expect("Error loading ciphers").from_db() + .load::(conn).expect("Error loading ciphers") }} } @@ -669,7 +664,7 @@ impl Cipher { db_run! {conn: { ciphers::table .filter(ciphers::organization_uuid.eq(org_uuid)) - .load::(conn).expect("Error loading ciphers").from_db() + .load::(conn).expect("Error loading ciphers") }} } @@ -689,7 +684,7 @@ impl Cipher { folders_ciphers::table.inner_join(ciphers::table) .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) .select(ciphers::all_columns) - .load::(conn).expect("Error loading ciphers").from_db() + .load::(conn).expect("Error loading ciphers") }} } @@ -698,7 +693,7 @@ impl Cipher { db_run! {conn: { ciphers::table .filter(ciphers::deleted_at.lt(dt)) - .load::(conn).expect("Error loading ciphers").from_db() + .load::(conn).expect("Error loading ciphers") }} } diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index f2b04ce537..bd6de300b2 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -1,35 +1,35 @@ use serde_json::Value; -use super::{CollectionGroup, User, UserOrgStatus, UserOrgType, UserOrganization}; - -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = collections)] - #[diesel(primary_key(uuid))] - pub struct Collection { - pub uuid: String, - pub org_uuid: String, - pub name: String, - pub external_id: Option, - } - - #[derive(Identifiable, Queryable, Insertable)] - #[diesel(table_name = users_collections)] - #[diesel(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(Identifiable, Queryable, Insertable)] - #[diesel(table_name = ciphers_collections)] - #[diesel(primary_key(cipher_uuid, collection_uuid))] - pub struct CollectionCipher { - pub cipher_uuid: String, - pub collection_uuid: String, - } +use super::{CollectionGroup, User, UserOrganization}; + +use crate::db::schema::{ciphers_collections, collections, users_collections}; + +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = collections)] +#[diesel(primary_key(uuid))] +pub struct Collection { + pub uuid: String, + pub org_uuid: String, + pub name: String, + pub external_id: Option, +} + +#[derive(Identifiable, Queryable, Insertable)] +#[diesel(table_name = users_collections)] +#[diesel(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(Identifiable, Queryable, Insertable)] +#[diesel(table_name = ciphers_collections)] +#[diesel(primary_key(cipher_uuid, collection_uuid))] +pub struct CollectionCipher { + pub cipher_uuid: String, + pub collection_uuid: String, } /// Local methods @@ -116,7 +116,7 @@ impl Collection { db_run! { conn: sqlite, mysql { match diesel::replace_into(collections::table) - .values(CollectionDb::to_db(self)) + .values(self) .execute(conn) { Ok(_) => Ok(()), @@ -124,7 +124,7 @@ impl Collection { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(collections::table) .filter(collections::uuid.eq(&self.uuid)) - .set(CollectionDb::to_db(self)) + .set(self) .execute(conn) .map_res("Error saving collection") } @@ -132,12 +132,11 @@ impl Collection { }.map_res("Error saving collection") } postgresql { - let value = CollectionDb::to_db(self); diesel::insert_into(collections::table) - .values(&value) + .values(self) .on_conflict(collections::uuid) .do_update() - .set(&value) + .set(self) .execute(conn) .map_res("Error saving collection") } @@ -174,9 +173,8 @@ impl Collection { db_run! { conn: { collections::table .filter(collections::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -220,7 +218,7 @@ impl Collection { ) .select(collections::all_columns) .distinct() - .load::(conn).expect("Error loading collections").from_db() + .load::(conn).expect("Error loading collections") }} } @@ -247,9 +245,8 @@ impl Collection { db_run! { conn: { collections::table .filter(collections::org_uuid.eq(org_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading collections") - .from_db() }} } @@ -270,9 +267,8 @@ impl Collection { .filter(collections::uuid.eq(uuid)) .filter(collections::org_uuid.eq(org_uuid)) .select(collections::all_columns) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -313,8 +309,7 @@ impl Collection { ) ) ).select(collections::all_columns) - .first::(conn).ok() - .from_db() + .first::(conn).ok() }} } @@ -420,9 +415,8 @@ impl CollectionUser { .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::(conn) + .load::(conn) .expect("Error loading users_collections") - .from_db() }} } @@ -433,9 +427,8 @@ impl CollectionUser { .filter(collections::org_uuid.eq(org_uuid)) .inner_join(users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid))) .select((users_organizations::uuid, users_collections::collection_uuid, users_collections::read_only, users_collections::hide_passwords)) - .load::(conn) + .load::(conn) .expect("Error loading users_collections") - .from_db() }} } @@ -516,9 +509,8 @@ impl CollectionUser { users_collections::table .filter(users_collections::collection_uuid.eq(collection_uuid)) .select(users_collections::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading users_collections") - .from_db() }} } @@ -531,9 +523,8 @@ impl CollectionUser { .filter(users_collections::collection_uuid.eq(collection_uuid)) .inner_join(users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid))) .select((users_organizations::uuid, users_collections::collection_uuid, users_collections::read_only, users_collections::hide_passwords)) - .load::(conn) + .load::(conn) .expect("Error loading users_collections") - .from_db() }} } @@ -547,9 +538,8 @@ impl CollectionUser { .filter(users_collections::collection_uuid.eq(collection_uuid)) .filter(users_collections::user_uuid.eq(user_uuid)) .select(users_collections::all_columns) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -558,9 +548,8 @@ impl CollectionUser { users_collections::table .filter(users_collections::user_uuid.eq(user_uuid)) .select(users_collections::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading users_collections") - .from_db() }} } diff --git a/src/db/models/device.rs b/src/db/models/device.rs index b80b47a104..431bc96daa 100644 --- a/src/db/models/device.rs +++ b/src/db/models/device.rs @@ -1,29 +1,28 @@ use chrono::{NaiveDateTime, Utc}; +use crate::db::schema::devices; use crate::{crypto, CONFIG}; use core::fmt; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = devices)] - #[diesel(treat_none_as_null = true)] - #[diesel(primary_key(uuid, user_uuid))] - pub struct Device { - pub uuid: String, - pub created_at: NaiveDateTime, - pub updated_at: NaiveDateTime, +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = devices)] +#[diesel(treat_none_as_null = true)] +#[diesel(primary_key(uuid, user_uuid))] +pub struct Device { + pub uuid: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, - pub user_uuid: String, + pub user_uuid: String, - pub name: String, - pub atype: i32, // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs - pub push_uuid: Option, - pub push_token: Option, + pub name: String, + pub atype: i32, // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs + pub push_uuid: Option, + pub push_token: Option, - pub refresh_token: String, + pub refresh_token: String, - pub twofactor_remember: Option, - } + pub twofactor_remember: Option, } /// Local methods @@ -121,14 +120,13 @@ impl Device { db_run! { conn: sqlite, mysql { crate::util::retry( - || diesel::replace_into(devices::table).values(DeviceDb::to_db(self)).execute(conn), + || diesel::replace_into(devices::table).values(&*self).execute(conn), 10, ).map_res("Error saving device") } postgresql { - let value = DeviceDb::to_db(self); crate::util::retry( - || diesel::insert_into(devices::table).values(&value).on_conflict((devices::uuid, devices::user_uuid)).do_update().set(&value).execute(conn), + || diesel::insert_into(devices::table).values(&*self).on_conflict((devices::uuid, devices::user_uuid)).do_update().set(&*self).execute(conn), 10, ).map_res("Error saving device") } @@ -148,9 +146,8 @@ impl Device { devices::table .filter(devices::uuid.eq(uuid)) .filter(devices::user_uuid.eq(user_uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -158,9 +155,8 @@ impl Device { db_run! { conn: { devices::table .filter(devices::user_uuid.eq(user_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading devices") - .from_db() }} } @@ -168,9 +164,8 @@ impl Device { db_run! { conn: { devices::table .filter(devices::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -187,9 +182,8 @@ impl Device { db_run! { conn: { devices::table .filter(devices::refresh_token.eq(refresh_token)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -198,9 +192,8 @@ impl Device { devices::table .filter(devices::user_uuid.eq(user_uuid)) .order(devices::updated_at.desc()) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } pub async fn find_push_devices_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { @@ -208,9 +201,8 @@ impl Device { devices::table .filter(devices::user_uuid.eq(user_uuid)) .filter(devices::push_token.is_not_null()) - .load::(conn) + .load::(conn) .expect("Error loading push devices") - .from_db() }} } diff --git a/src/db/models/emergency_access.rs b/src/db/models/emergency_access.rs index ccb21e5b70..64dbb94e56 100644 --- a/src/db/models/emergency_access.rs +++ b/src/db/models/emergency_access.rs @@ -4,26 +4,25 @@ use serde_json::Value; use crate::{api::EmptyResult, db::DbConn, error::MapResult}; use super::User; - -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = emergency_access)] - #[diesel(treat_none_as_null = true)] - #[diesel(primary_key(uuid))] - pub struct EmergencyAccess { - pub uuid: String, - pub grantor_uuid: String, - pub grantee_uuid: Option, - pub email: Option, - pub key_encrypted: Option, - pub atype: i32, //EmergencyAccessType - pub status: i32, //EmergencyAccessStatus - pub wait_time_days: i32, - pub recovery_initiated_at: Option, - pub last_notification_at: Option, - pub updated_at: NaiveDateTime, - pub created_at: NaiveDateTime, - } +use crate::db::schema::emergency_access; + +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = emergency_access)] +#[diesel(treat_none_as_null = true)] +#[diesel(primary_key(uuid))] +pub struct EmergencyAccess { + pub uuid: String, + pub grantor_uuid: String, + pub grantee_uuid: Option, + pub email: Option, + pub key_encrypted: Option, + pub atype: i32, //EmergencyAccessType + pub status: i32, //EmergencyAccessStatus + pub wait_time_days: i32, + pub recovery_initiated_at: Option, + pub last_notification_at: Option, + pub updated_at: NaiveDateTime, + pub created_at: NaiveDateTime, } /// Local methods @@ -136,16 +135,16 @@ impl EmergencyAccess { db_run! { conn: sqlite, mysql { - match diesel::replace_into(emergency_access::table) - .values(EmergencyAccessDb::to_db(self)) + match diesel::replace_into(schema::emergency_access::table) + .values(&*self) .execute(conn) { Ok(_) => Ok(()), // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { - diesel::update(emergency_access::table) - .filter(emergency_access::uuid.eq(&self.uuid)) - .set(EmergencyAccessDb::to_db(self)) + diesel::update(schema::emergency_access::table) + .filter(schema::emergency_access::uuid.eq(&self.uuid)) + .set(&*self) .execute(conn) .map_res("Error updating emergency access") } @@ -153,12 +152,11 @@ impl EmergencyAccess { }.map_res("Error saving emergency access") } postgresql { - let value = EmergencyAccessDb::to_db(self); - diesel::insert_into(emergency_access::table) - .values(&value) - .on_conflict(emergency_access::uuid) + diesel::insert_into(schema::emergency_access::table) + .values(&*self) + .on_conflict(schema::emergency_access::uuid) .do_update() - .set(&value) + .set(&*self) .execute(conn) .map_res("Error saving emergency access") } @@ -178,8 +176,8 @@ impl EmergencyAccess { db_run! {conn: { crate::util::retry(|| { - diesel::update(emergency_access::table.filter(emergency_access::uuid.eq(&self.uuid))) - .set((emergency_access::status.eq(status), emergency_access::updated_at.eq(date))) + diesel::update(schema::emergency_access::table.filter(schema::emergency_access::uuid.eq(&self.uuid))) + .set((schema::emergency_access::status.eq(status), schema::emergency_access::updated_at.eq(date))) .execute(conn) }, 10) .map_res("Error updating emergency access status") @@ -196,8 +194,8 @@ impl EmergencyAccess { db_run! {conn: { crate::util::retry(|| { - diesel::update(emergency_access::table.filter(emergency_access::uuid.eq(&self.uuid))) - .set((emergency_access::last_notification_at.eq(date), emergency_access::updated_at.eq(date))) + diesel::update(schema::emergency_access::table.filter(schema::emergency_access::uuid.eq(&self.uuid))) + .set((schema::emergency_access::last_notification_at.eq(date), schema::emergency_access::updated_at.eq(date))) .execute(conn) }, 10) .map_res("Error updating emergency access status") @@ -218,7 +216,7 @@ impl EmergencyAccess { User::update_uuid_revision(&self.grantor_uuid, conn).await; db_run! { conn: { - diesel::delete(emergency_access::table.filter(emergency_access::uuid.eq(self.uuid))) + diesel::delete(schema::emergency_access::table.filter(schema::emergency_access::uuid.eq(self.uuid))) .execute(conn) .map_res("Error removing user from emergency access") }} @@ -226,10 +224,10 @@ impl EmergencyAccess { pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { - emergency_access::table - .filter(emergency_access::uuid.eq(uuid)) - .first::(conn) - .ok().from_db() + schema::emergency_access::table + .filter(schema::emergency_access::uuid.eq(uuid)) + .first::(conn) + .ok() }} } @@ -240,56 +238,56 @@ impl EmergencyAccess { conn: &mut DbConn, ) -> Option { db_run! { conn: { - emergency_access::table - .filter(emergency_access::grantor_uuid.eq(grantor_uuid)) - .filter(emergency_access::grantee_uuid.eq(grantee_uuid).or(emergency_access::email.eq(email))) - .first::(conn) - .ok().from_db() + schema::emergency_access::table + .filter(schema::emergency_access::grantor_uuid.eq(grantor_uuid)) + .filter(schema::emergency_access::grantee_uuid.eq(grantee_uuid).or(schema::emergency_access::email.eq(email))) + .first::(conn) + .ok() }} } pub async fn find_all_recoveries_initiated(conn: &mut DbConn) -> Vec { db_run! { conn: { - emergency_access::table - .filter(emergency_access::status.eq(EmergencyAccessStatus::RecoveryInitiated as i32)) - .filter(emergency_access::recovery_initiated_at.is_not_null()) - .load::(conn).expect("Error loading emergency_access").from_db() + schema::emergency_access::table + .filter(schema::emergency_access::status.eq(EmergencyAccessStatus::RecoveryInitiated as i32)) + .filter(schema::emergency_access::recovery_initiated_at.is_not_null()) + .load::(conn).expect("Error loading emergency_access") }} } pub async fn find_by_uuid_and_grantor_uuid(uuid: &str, grantor_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { - emergency_access::table - .filter(emergency_access::uuid.eq(uuid)) - .filter(emergency_access::grantor_uuid.eq(grantor_uuid)) - .first::(conn) - .ok().from_db() + schema::emergency_access::table + .filter(schema::emergency_access::uuid.eq(uuid)) + .filter(schema::emergency_access::grantor_uuid.eq(grantor_uuid)) + .first::(conn) + .ok() }} } pub async fn find_all_by_grantee_uuid(grantee_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { - emergency_access::table - .filter(emergency_access::grantee_uuid.eq(grantee_uuid)) - .load::(conn).expect("Error loading emergency_access").from_db() + schema::emergency_access::table + .filter(schema::emergency_access::grantee_uuid.eq(grantee_uuid)) + .load::(conn).expect("Error loading emergency_access") }} } pub async fn find_invited_by_grantee_email(grantee_email: &str, conn: &mut DbConn) -> Option { db_run! { conn: { - emergency_access::table - .filter(emergency_access::email.eq(grantee_email)) - .filter(emergency_access::status.eq(EmergencyAccessStatus::Invited as i32)) - .first::(conn) - .ok().from_db() + schema::emergency_access::table + .filter(schema::emergency_access::email.eq(grantee_email)) + .filter(schema::emergency_access::status.eq(EmergencyAccessStatus::Invited as i32)) + .first::(conn) + .ok() }} } pub async fn find_all_by_grantor_uuid(grantor_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { - emergency_access::table - .filter(emergency_access::grantor_uuid.eq(grantor_uuid)) - .load::(conn).expect("Error loading emergency_access").from_db() + schema::emergency_access::table + .filter(schema::emergency_access::grantor_uuid.eq(grantor_uuid)) + .load::(conn).expect("Error loading emergency_access") }} } } diff --git a/src/db/models/event.rs b/src/db/models/event.rs index af2f6c66ba..8be3aa5395 100644 --- a/src/db/models/event.rs +++ b/src/db/models/event.rs @@ -6,33 +6,32 @@ use crate::{api::EmptyResult, error::MapResult, CONFIG}; use chrono::{Duration, NaiveDateTime, Utc}; // https://bitwarden.com/help/event-logs/ +use crate::db::schema::event; -db_object! { - // Upstream: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Services/Implementations/EventService.cs - // Upstream: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Api/Models/Public/Response/EventResponseModel.cs - // Upstream SQL: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Sql/dbo/Tables/Event.sql - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = event)] - #[diesel(primary_key(uuid))] - pub struct Event { - pub uuid: String, - pub event_type: i32, // EventType - pub user_uuid: Option, - pub org_uuid: Option, - pub cipher_uuid: Option, - pub collection_uuid: Option, - pub group_uuid: Option, - pub org_user_uuid: Option, - pub act_user_uuid: Option, - // Upstream enum: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Enums/DeviceType.cs - pub device_type: Option, - pub ip_address: Option, - pub event_date: NaiveDateTime, - pub policy_uuid: Option, - pub provider_uuid: Option, - pub provider_user_uuid: Option, - pub provider_org_uuid: Option, - } +// Upstream: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Services/Implementations/EventService.cs +// Upstream: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Api/Models/Public/Response/EventResponseModel.cs +// Upstream SQL: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Sql/dbo/Tables/Event.sql +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = event)] +#[diesel(primary_key(uuid))] +pub struct Event { + pub uuid: String, + pub event_type: i32, // EventType + pub user_uuid: Option, + pub org_uuid: Option, + pub cipher_uuid: Option, + pub collection_uuid: Option, + pub group_uuid: Option, + pub org_user_uuid: Option, + pub act_user_uuid: Option, + // Upstream enum: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Enums/DeviceType.cs + pub device_type: Option, + pub ip_address: Option, + pub event_date: NaiveDateTime, + pub policy_uuid: Option, + pub provider_uuid: Option, + pub provider_user_uuid: Option, + pub provider_org_uuid: Option, } // Upstream enum: https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Enums/EventType.cs @@ -181,17 +180,17 @@ impl Event { pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: sqlite, mysql { - diesel::replace_into(event::table) - .values(EventDb::to_db(self)) + diesel::replace_into(schema::event::table) + .values(self) .execute(conn) .map_res("Error saving event") } postgresql { - diesel::insert_into(event::table) - .values(EventDb::to_db(self)) - .on_conflict(event::uuid) + diesel::insert_into(schema::event::table) + .values(self) + .on_conflict(schema::event::uuid) .do_update() - .set(EventDb::to_db(self)) + .set(self) .execute(conn) .map_res("Error saving event") } @@ -208,24 +207,22 @@ impl Event { // We loop through the events here and insert them one at a time. sqlite { for event in events { - diesel::insert_or_ignore_into(event::table) - .values(EventDb::to_db(&event)) + diesel::insert_or_ignore_into(schema::event::table) + .values(&event) .execute(conn) .unwrap_or_default(); } Ok(()) } mysql { - let events: Vec = events.iter().map(EventDb::to_db).collect(); - diesel::insert_or_ignore_into(event::table) + diesel::insert_or_ignore_into(schema::event::table) .values(&events) .execute(conn) .unwrap_or_default(); Ok(()) } postgresql { - let events: Vec = events.iter().map(EventDb::to_db).collect(); - diesel::insert_into(event::table) + diesel::insert_into(schema::event::table) .values(&events) .on_conflict_do_nothing() .execute(conn) @@ -237,7 +234,7 @@ impl Event { pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { - diesel::delete(event::table.filter(event::uuid.eq(self.uuid))) + diesel::delete(schema::event::table.filter(schema::event::uuid.eq(self.uuid))) .execute(conn) .map_res("Error deleting event") }} @@ -252,21 +249,20 @@ impl Event { conn: &mut DbConn, ) -> Vec { db_run! { conn: { - event::table - .filter(event::org_uuid.eq(org_uuid)) - .filter(event::event_date.between(start, end)) - .order_by(event::event_date.desc()) + schema::event::table + .filter(schema::event::org_uuid.eq(org_uuid)) + .filter(schema::event::event_date.between(start, end)) + .order_by(schema::event::event_date.desc()) .limit(Self::PAGE_SIZE) - .load::(conn) + .load::(conn) .expect("Error filtering events") - .from_db() }} } pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { db_run! { conn: { - event::table - .filter(event::org_uuid.eq(org_uuid)) + schema::event::table + .filter(schema::event::org_uuid.eq(org_uuid)) .count() .first::(conn) .ok() @@ -282,17 +278,16 @@ impl Event { conn: &mut DbConn, ) -> Vec { db_run! { conn: { - event::table + schema::event::table .inner_join(users_organizations::table.on(users_organizations::uuid.eq(user_org_uuid))) - .filter(event::org_uuid.eq(org_uuid)) - .filter(event::event_date.between(start, end)) - .filter(event::user_uuid.eq(users_organizations::user_uuid.nullable()).or(event::act_user_uuid.eq(users_organizations::user_uuid.nullable()))) - .select(event::all_columns) - .order_by(event::event_date.desc()) + .filter(schema::event::org_uuid.eq(org_uuid)) + .filter(schema::event::event_date.between(start, end)) + .filter(schema::event::user_uuid.eq(users_organizations::user_uuid.nullable()).or(schema::event::act_user_uuid.eq(users_organizations::user_uuid.nullable()))) + .select(schema::event::all_columns) + .order_by(schema::event::event_date.desc()) .limit(Self::PAGE_SIZE) - .load::(conn) + .load::(conn) .expect("Error filtering events") - .from_db() }} } @@ -303,14 +298,13 @@ impl Event { conn: &mut DbConn, ) -> Vec { db_run! { conn: { - event::table - .filter(event::cipher_uuid.eq(cipher_uuid)) - .filter(event::event_date.between(start, end)) - .order_by(event::event_date.desc()) + schema::event::table + .filter(schema::event::cipher_uuid.eq(cipher_uuid)) + .filter(schema::event::event_date.between(start, end)) + .order_by(schema::event::event_date.desc()) .limit(Self::PAGE_SIZE) - .load::(conn) + .load::(conn) .expect("Error filtering events") - .from_db() }} } @@ -318,7 +312,7 @@ impl Event { if let Some(days_to_retain) = CONFIG.events_days_retain() { let dt = Utc::now().naive_utc() - Duration::days(days_to_retain); db_run! { conn: { - diesel::delete(event::table.filter(event::event_date.lt(dt))) + diesel::delete(schema::event::table.filter(schema::event::event_date.lt(dt))) .execute(conn) .map_res("Error cleaning old events") }} diff --git a/src/db/models/favorite.rs b/src/db/models/favorite.rs index a301f5977b..c726b5d4a9 100644 --- a/src/db/models/favorite.rs +++ b/src/db/models/favorite.rs @@ -1,13 +1,11 @@ use super::User; - -db_object! { - #[derive(Identifiable, Queryable, Insertable)] - #[diesel(table_name = favorites)] - #[diesel(primary_key(user_uuid, cipher_uuid))] - pub struct Favorite { - pub user_uuid: String, - pub cipher_uuid: String, - } +use crate::db::schema::favorites; +#[derive(Identifiable, Queryable, Insertable)] +#[diesel(table_name = favorites)] +#[diesel(primary_key(user_uuid, cipher_uuid))] +pub struct Favorite { + pub user_uuid: String, + pub cipher_uuid: String, } use crate::db::DbConn; @@ -64,7 +62,7 @@ impl Favorite { // Delete all favorite entries associated with the specified cipher. pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { - diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid))) + diesel::delete(schema::favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid))) .execute(conn) .map_res("Error removing favorites by cipher") }} @@ -73,7 +71,7 @@ impl Favorite { // Delete all favorite entries associated with the specified user. pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { - diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid))) + diesel::delete(schema::favorites::table.filter(favorites::user_uuid.eq(user_uuid))) .execute(conn) .map_res("Error removing favorites by user") }} @@ -83,7 +81,7 @@ impl Favorite { /// This is used during a full sync so we only need one query for all favorite cipher matches. pub async fn get_all_cipher_uuid_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { - favorites::table + schema::favorites::table .filter(favorites::user_uuid.eq(user_uuid)) .select(favorites::cipher_uuid) .load::(conn) diff --git a/src/db/models/folder.rs b/src/db/models/folder.rs index 9385e78de5..eacd86049d 100644 --- a/src/db/models/folder.rs +++ b/src/db/models/folder.rs @@ -2,26 +2,25 @@ use chrono::{NaiveDateTime, Utc}; use serde_json::Value; use super::User; +use crate::db::schema::{folders, folders_ciphers}; + +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = folders)] +#[diesel(primary_key(uuid))] +pub struct Folder { + pub uuid: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub user_uuid: String, + pub name: String, +} -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = folders)] - #[diesel(primary_key(uuid))] - pub struct Folder { - pub uuid: String, - pub created_at: NaiveDateTime, - pub updated_at: NaiveDateTime, - pub user_uuid: String, - pub name: String, - } - - #[derive(Identifiable, Queryable, Insertable)] - #[diesel(table_name = folders_ciphers)] - #[diesel(primary_key(cipher_uuid, folder_uuid))] - pub struct FolderCipher { - pub cipher_uuid: String, - pub folder_uuid: String, - } +#[derive(Identifiable, Queryable, Insertable)] +#[diesel(table_name = folders_ciphers)] +#[diesel(primary_key(cipher_uuid, folder_uuid))] +pub struct FolderCipher { + pub cipher_uuid: String, + pub folder_uuid: String, } /// Local methods @@ -74,7 +73,7 @@ impl Folder { db_run! { conn: sqlite, mysql { match diesel::replace_into(folders::table) - .values(FolderDb::to_db(self)) + .values(&*self) .execute(conn) { Ok(_) => Ok(()), @@ -82,7 +81,7 @@ impl Folder { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(folders::table) .filter(folders::uuid.eq(&self.uuid)) - .set(FolderDb::to_db(self)) + .set(&*self) .execute(conn) .map_res("Error saving folder") } @@ -90,12 +89,11 @@ impl Folder { }.map_res("Error saving folder") } postgresql { - let value = FolderDb::to_db(self); diesel::insert_into(folders::table) - .values(&value) + .values(&*self) .on_conflict(folders::uuid) .do_update() - .set(&value) + .set(&*self) .execute(conn) .map_res("Error saving folder") } @@ -124,9 +122,8 @@ impl Folder { db_run! { conn: { folders::table .filter(folders::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -134,9 +131,8 @@ impl Folder { db_run! { conn: { folders::table .filter(folders::user_uuid.eq(user_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading folders") - .from_db() }} } } @@ -149,13 +145,13 @@ impl FolderCipher { // Table folders_ciphers does not have ForeignKey Constraints which would cause conflicts. // This table has no constraints pointing to itself, but only to others. diesel::replace_into(folders_ciphers::table) - .values(FolderCipherDb::to_db(self)) + .values(self) .execute(conn) .map_res("Error adding cipher to folder") } postgresql { diesel::insert_into(folders_ciphers::table) - .values(FolderCipherDb::to_db(self)) + .values(self) .on_conflict((folders_ciphers::cipher_uuid, folders_ciphers::folder_uuid)) .do_nothing() .execute(conn) @@ -197,9 +193,8 @@ impl FolderCipher { folders_ciphers::table .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) .filter(folders_ciphers::cipher_uuid.eq(cipher_uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -207,9 +202,8 @@ impl FolderCipher { db_run! { conn: { folders_ciphers::table .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading folders") - .from_db() }} } diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 670e311454..e50c6eb1c7 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -1,37 +1,36 @@ +use crate::db::schema::{collections_groups, groups, groups_users}; use chrono::{NaiveDateTime, Utc}; use serde_json::Value; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = groups)] - #[diesel(primary_key(uuid))] - pub struct Group { - pub uuid: String, - pub organizations_uuid: String, - pub name: String, - pub access_all: bool, - pub external_id: Option, - pub creation_date: NaiveDateTime, - pub revision_date: NaiveDateTime, - } +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = groups)] +#[diesel(primary_key(uuid))] +pub struct Group { + pub uuid: String, + pub organizations_uuid: String, + pub name: String, + pub access_all: bool, + pub external_id: Option, + pub creation_date: NaiveDateTime, + pub revision_date: NaiveDateTime, +} - #[derive(Identifiable, Queryable, Insertable)] - #[diesel(table_name = collections_groups)] - #[diesel(primary_key(collections_uuid, groups_uuid))] - pub struct CollectionGroup { - pub collections_uuid: String, - pub groups_uuid: String, - pub read_only: bool, - pub hide_passwords: bool, - } +#[derive(Identifiable, Queryable, Insertable)] +#[diesel(table_name = collections_groups)] +#[diesel(primary_key(collections_uuid, groups_uuid))] +pub struct CollectionGroup { + pub collections_uuid: String, + pub groups_uuid: String, + pub read_only: bool, + pub hide_passwords: bool, +} - #[derive(Identifiable, Queryable, Insertable)] - #[diesel(table_name = groups_users)] - #[diesel(primary_key(groups_uuid, users_organizations_uuid))] - pub struct GroupUser { - pub groups_uuid: String, - pub users_organizations_uuid: String - } +#[derive(Identifiable, Queryable, Insertable)] +#[diesel(table_name = groups_users)] +#[diesel(primary_key(groups_uuid, users_organizations_uuid))] +pub struct GroupUser { + pub groups_uuid: String, + pub users_organizations_uuid: String, } /// Local methods @@ -137,7 +136,7 @@ impl Group { db_run! { conn: sqlite, mysql { match diesel::replace_into(groups::table) - .values(GroupDb::to_db(self)) + .values(&*self) .execute(conn) { Ok(_) => Ok(()), @@ -145,7 +144,7 @@ impl Group { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(groups::table) .filter(groups::uuid.eq(&self.uuid)) - .set(GroupDb::to_db(self)) + .set(&*self) .execute(conn) .map_res("Error saving group") } @@ -153,12 +152,11 @@ impl Group { }.map_res("Error saving group") } postgresql { - let value = GroupDb::to_db(self); diesel::insert_into(groups::table) - .values(&value) + .values(&*self) .on_conflict(groups::uuid) .do_update() - .set(&value) + .set(&*self) .execute(conn) .map_res("Error saving group") } @@ -176,9 +174,8 @@ impl Group { db_run! { conn: { groups::table .filter(groups::organizations_uuid.eq(organizations_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading groups") - .from_db() }} } @@ -197,9 +194,8 @@ impl Group { db_run! { conn: { groups::table .filter(groups::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -207,9 +203,8 @@ impl Group { db_run! { conn: { groups::table .filter(groups::external_id.eq(id)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } //Returns all organizations the user has full access to @@ -338,9 +333,8 @@ impl CollectionGroup { db_run! { conn: { collections_groups::table .filter(collections_groups::groups_uuid.eq(group_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading collection groups") - .from_db() }} } @@ -355,9 +349,8 @@ impl CollectionGroup { )) .filter(users_organizations::user_uuid.eq(user_uuid)) .select(collections_groups::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading user collection groups") - .from_db() }} } @@ -366,9 +359,8 @@ impl CollectionGroup { collections_groups::table .filter(collections_groups::collections_uuid.eq(collection_uuid)) .select(collections_groups::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading collection groups") - .from_db() }} } @@ -470,9 +462,8 @@ impl GroupUser { db_run! { conn: { groups_users::table .filter(groups_users::groups_uuid.eq(group_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading group users") - .from_db() }} } @@ -480,9 +471,8 @@ impl GroupUser { db_run! { conn: { groups_users::table .filter(groups_users::users_organizations_uuid.eq(users_organizations_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading groups for user") - .from_db() }} } diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 8b3f1271af..31b3db138a 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -6,19 +6,18 @@ use crate::db::DbConn; use crate::error::MapResult; use crate::util::UpCase; -use super::{TwoFactor, UserOrgStatus, UserOrgType, UserOrganization}; +use super::{TwoFactor, UserOrgType, UserOrganization}; +use crate::db::schema::org_policies; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = org_policies)] - #[diesel(primary_key(uuid))] - pub struct OrgPolicy { - pub uuid: String, - pub org_uuid: String, - pub atype: i32, - pub enabled: bool, - pub data: String, - } +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = org_policies)] +#[diesel(primary_key(uuid))] +pub struct OrgPolicy { + pub uuid: String, + pub org_uuid: String, + pub atype: i32, + pub enabled: bool, + pub data: String, } // https://github.com/bitwarden/server/blob/b86a04cef9f1e1b82cf18e49fc94e017c641130c/src/Core/Enums/PolicyType.cs @@ -94,7 +93,7 @@ impl OrgPolicy { db_run! { conn: sqlite, mysql { match diesel::replace_into(org_policies::table) - .values(OrgPolicyDb::to_db(self)) + .values(self) .execute(conn) { Ok(_) => Ok(()), @@ -102,7 +101,7 @@ impl OrgPolicy { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(org_policies::table) .filter(org_policies::uuid.eq(&self.uuid)) - .set(OrgPolicyDb::to_db(self)) + .set(self) .execute(conn) .map_res("Error saving org_policy") } @@ -110,7 +109,6 @@ impl OrgPolicy { }.map_res("Error saving org_policy") } postgresql { - let value = OrgPolicyDb::to_db(self); // We need to make sure we're not going to violate the unique constraint on org_uuid and atype. // This happens automatically on other DBMS backends due to replace_into(). PostgreSQL does // not support multiple constraints on ON CONFLICT clauses. @@ -123,10 +121,10 @@ impl OrgPolicy { .map_res("Error deleting org_policy for insert")?; diesel::insert_into(org_policies::table) - .values(&value) + .values(self) .on_conflict(org_policies::uuid) .do_update() - .set(&value) + .set(self) .execute(conn) .map_res("Error saving org_policy") } @@ -145,9 +143,8 @@ impl OrgPolicy { db_run! { conn: { org_policies::table .filter(org_policies::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -155,9 +152,8 @@ impl OrgPolicy { db_run! { conn: { org_policies::table .filter(org_policies::org_uuid.eq(org_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading org_policy") - .from_db() }} } @@ -173,9 +169,8 @@ impl OrgPolicy { users_organizations::status.eq(UserOrgStatus::Confirmed as i32) ) .select(org_policies::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading org_policy") - .from_db() }} } @@ -184,9 +179,8 @@ impl OrgPolicy { org_policies::table .filter(org_policies::org_uuid.eq(org_uuid)) .filter(org_policies::atype.eq(policy_type as i32)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -219,9 +213,8 @@ impl OrgPolicy { .filter(org_policies::atype.eq(policy_type as i32)) .filter(org_policies::enabled.eq(true)) .select(org_policies::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading org_policy") - .from_db() }} } @@ -243,9 +236,8 @@ impl OrgPolicy { .filter(org_policies::atype.eq(policy_type as i32)) .filter(org_policies::enabled.eq(true)) .select(org_policies::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading org_policy") - .from_db() }} } diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index 32ffc43731..3edd1171dd 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -4,45 +4,44 @@ use serde_json::Value; use std::cmp::Ordering; use super::{CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User}; +use crate::db::schema::{organization_api_key, organizations, users_organizations}; use crate::CONFIG; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = organizations)] - #[diesel(primary_key(uuid))] - pub struct Organization { - pub uuid: String, - pub name: String, - pub billing_email: String, - pub private_key: Option, - pub public_key: Option, - } - - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = users_organizations)] - #[diesel(primary_key(uuid))] - pub struct UserOrganization { - pub uuid: String, - pub user_uuid: String, - pub org_uuid: String, - - pub access_all: bool, - pub akey: String, - pub status: i32, - pub atype: i32, - pub reset_password_key: Option, - } - - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = organization_api_key)] - #[diesel(primary_key(uuid, org_uuid))] - pub struct OrganizationApiKey { - pub uuid: String, - pub org_uuid: String, - pub atype: i32, - pub api_key: String, - pub revision_date: NaiveDateTime, - } +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = organizations)] +#[diesel(primary_key(uuid))] +pub struct Organization { + pub uuid: String, + pub name: String, + pub billing_email: String, + pub private_key: Option, + pub public_key: Option, +} + +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = users_organizations)] +#[diesel(primary_key(uuid))] +pub struct UserOrganization { + pub uuid: String, + pub user_uuid: String, + pub org_uuid: String, + + pub access_all: bool, + pub akey: String, + pub status: i32, + pub atype: i32, + pub reset_password_key: Option, +} + +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = organization_api_key)] +#[diesel(primary_key(uuid, org_uuid))] +pub struct OrganizationApiKey { + pub uuid: String, + pub org_uuid: String, + pub atype: i32, + pub api_key: String, + pub revision_date: NaiveDateTime, } // https://github.com/bitwarden/server/blob/b86a04cef9f1e1b82cf18e49fc94e017c641130c/src/Core/Enums/OrganizationUserStatusType.cs @@ -260,7 +259,7 @@ impl Organization { db_run! { conn: sqlite, mysql { match diesel::replace_into(organizations::table) - .values(OrganizationDb::to_db(self)) + .values(self) .execute(conn) { Ok(_) => Ok(()), @@ -268,7 +267,7 @@ impl Organization { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(organizations::table) .filter(organizations::uuid.eq(&self.uuid)) - .set(OrganizationDb::to_db(self)) + .set(self) .execute(conn) .map_res("Error saving organization") } @@ -277,12 +276,11 @@ impl Organization { } postgresql { - let value = OrganizationDb::to_db(self); diesel::insert_into(organizations::table) - .values(&value) + .values(self) .on_conflict(organizations::uuid) .do_update() - .set(&value) + .set(self) .execute(conn) .map_res("Error saving organization") } @@ -309,14 +307,14 @@ impl Organization { db_run! { conn: { organizations::table .filter(organizations::uuid.eq(uuid)) - .first::(conn) - .ok().from_db() + .first::(conn) + .ok() }} } pub async fn get_all(conn: &mut DbConn) -> Vec { db_run! { conn: { - organizations::table.load::(conn).expect("Error loading organizations").from_db() + organizations::table.load::(conn).expect("Error loading organizations") }} } } @@ -500,7 +498,7 @@ impl UserOrganization { db_run! { conn: sqlite, mysql { match diesel::replace_into(users_organizations::table) - .values(UserOrganizationDb::to_db(self)) + .values(self) .execute(conn) { Ok(_) => Ok(()), @@ -508,7 +506,7 @@ impl UserOrganization { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(users_organizations::table) .filter(users_organizations::uuid.eq(&self.uuid)) - .set(UserOrganizationDb::to_db(self)) + .set(self) .execute(conn) .map_res("Error adding user to organization") }, @@ -516,12 +514,11 @@ impl UserOrganization { }.map_res("Error adding user to organization") } postgresql { - let value = UserOrganizationDb::to_db(self); diesel::insert_into(users_organizations::table) - .values(&value) + .values(self) .on_conflict(users_organizations::uuid) .do_update() - .set(&value) + .set(self) .execute(conn) .map_res("Error adding user to organization") } @@ -581,8 +578,8 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::uuid.eq(uuid)) - .first::(conn) - .ok().from_db() + .first::(conn) + .ok() }} } @@ -591,8 +588,8 @@ impl UserOrganization { users_organizations::table .filter(users_organizations::uuid.eq(uuid)) .filter(users_organizations::org_uuid.eq(org_uuid)) - .first::(conn) - .ok().from_db() + .first::(conn) + .ok() }} } @@ -601,8 +598,8 @@ impl UserOrganization { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32)) - .load::(conn) - .unwrap_or_default().from_db() + .load::(conn) + .unwrap_or_default() }} } @@ -611,8 +608,8 @@ impl UserOrganization { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::status.eq(UserOrgStatus::Invited as i32)) - .load::(conn) - .unwrap_or_default().from_db() + .load::(conn) + .unwrap_or_default() }} } @@ -620,8 +617,8 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) - .load::(conn) - .unwrap_or_default().from_db() + .load::(conn) + .unwrap_or_default() }} } @@ -641,8 +638,8 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) - .load::(conn) - .expect("Error loading user organizations").from_db() + .load::(conn) + .expect("Error loading user organizations") }} } @@ -662,8 +659,8 @@ impl UserOrganization { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::atype.eq(atype as i32)) - .load::(conn) - .expect("Error loading user organizations").from_db() + .load::(conn) + .expect("Error loading user organizations") }} } @@ -684,8 +681,8 @@ impl UserOrganization { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid)) - .first::(conn) - .ok().from_db() + .first::(conn) + .ok() }} } @@ -693,8 +690,8 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) - .load::(conn) - .expect("Error loading user organizations").from_db() + .load::(conn) + .expect("Error loading user organizations") }} } @@ -722,8 +719,8 @@ impl UserOrganization { users_organizations::status.eq(UserOrgStatus::Confirmed as i32) ) .select(users_organizations::all_columns) - .load::(conn) - .unwrap_or_default().from_db() + .load::(conn) + .unwrap_or_default() }} } @@ -746,7 +743,7 @@ impl UserOrganization { ) .select(users_organizations::all_columns) .distinct() - .load::(conn).expect("Error loading user organizations").from_db() + .load::(conn).expect("Error loading user organizations") }} } @@ -775,7 +772,7 @@ impl UserOrganization { ) ) .select(users_organizations::all_columns) - .load::(conn).expect("Error loading user organizations").from_db() + .load::(conn).expect("Error loading user organizations") }} } } @@ -785,7 +782,7 @@ impl OrganizationApiKey { db_run! { conn: sqlite, mysql { match diesel::replace_into(organization_api_key::table) - .values(OrganizationApiKeyDb::to_db(self)) + .values(self) .execute(conn) { Ok(_) => Ok(()), @@ -793,7 +790,7 @@ impl OrganizationApiKey { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(organization_api_key::table) .filter(organization_api_key::uuid.eq(&self.uuid)) - .set(OrganizationApiKeyDb::to_db(self)) + .set(self) .execute(conn) .map_res("Error saving organization") } @@ -802,12 +799,11 @@ impl OrganizationApiKey { } postgresql { - let value = OrganizationApiKeyDb::to_db(self); diesel::insert_into(organization_api_key::table) - .values(&value) + .values(self) .on_conflict((organization_api_key::uuid, organization_api_key::org_uuid)) .do_update() - .set(&value) + .set(self) .execute(conn) .map_res("Error saving organization") } @@ -818,8 +814,8 @@ impl OrganizationApiKey { db_run! { conn: { organization_api_key::table .filter(organization_api_key::org_uuid.eq(org_uuid)) - .first::(conn) - .ok().from_db() + .first::(conn) + .ok() }} } } diff --git a/src/db/models/send.rs b/src/db/models/send.rs index 4975612578..0408b75fcc 100644 --- a/src/db/models/send.rs +++ b/src/db/models/send.rs @@ -2,40 +2,38 @@ use chrono::{NaiveDateTime, Utc}; use serde_json::Value; use super::User; - -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = sends)] - #[diesel(treat_none_as_null = true)] - #[diesel(primary_key(uuid))] - pub struct Send { - pub uuid: String, - - pub user_uuid: Option, - pub organization_uuid: Option, - - - pub name: String, - pub notes: Option, - - pub atype: i32, - pub data: String, - pub akey: String, - pub password_hash: Option>, - password_salt: Option>, - password_iter: Option, - - pub max_access_count: Option, - pub access_count: i32, - - pub creation_date: NaiveDateTime, - pub revision_date: NaiveDateTime, - pub expiration_date: Option, - pub deletion_date: NaiveDateTime, - - pub disabled: bool, - pub hide_email: Option, - } +use crate::db::schema::sends; + +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = sends)] +#[diesel(treat_none_as_null = true)] +#[diesel(primary_key(uuid))] +pub struct Send { + pub uuid: String, + + pub user_uuid: Option, + pub organization_uuid: Option, + + pub name: String, + pub notes: Option, + + pub atype: i32, + pub data: String, + pub akey: String, + pub password_hash: Option>, + password_salt: Option>, + password_iter: Option, + + pub max_access_count: Option, + pub access_count: i32, + + pub creation_date: NaiveDateTime, + pub revision_date: NaiveDateTime, + pub expiration_date: Option, + pub deletion_date: NaiveDateTime, + + pub disabled: bool, + pub hide_email: Option, } #[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)] @@ -181,7 +179,7 @@ impl Send { db_run! { conn: sqlite, mysql { match diesel::replace_into(sends::table) - .values(SendDb::to_db(self)) + .values(&*self) .execute(conn) { Ok(_) => Ok(()), @@ -189,7 +187,7 @@ impl Send { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(sends::table) .filter(sends::uuid.eq(&self.uuid)) - .set(SendDb::to_db(self)) + .set(&*self) .execute(conn) .map_res("Error saving send") } @@ -197,12 +195,11 @@ impl Send { }.map_res("Error saving send") } postgresql { - let value = SendDb::to_db(self); diesel::insert_into(sends::table) - .values(&value) + .values(&*self) .on_conflict(sends::uuid) .do_update() - .set(&value) + .set(&*self) .execute(conn) .map_res("Error saving send") } @@ -272,9 +269,8 @@ impl Send { db_run! {conn: { sends::table .filter(sends::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -282,7 +278,7 @@ impl Send { db_run! {conn: { sends::table .filter(sends::user_uuid.eq(user_uuid)) - .load::(conn).expect("Error loading sends").from_db() + .load::(conn).expect("Error loading sends") }} } @@ -290,7 +286,7 @@ impl Send { db_run! {conn: { sends::table .filter(sends::organization_uuid.eq(org_uuid)) - .load::(conn).expect("Error loading sends").from_db() + .load::(conn).expect("Error loading sends") }} } @@ -299,7 +295,7 @@ impl Send { db_run! {conn: { sends::table .filter(sends::deletion_date.lt(now)) - .load::(conn).expect("Error loading sends").from_db() + .load::(conn).expect("Error loading sends") }} } } diff --git a/src/db/models/two_factor.rs b/src/db/models/two_factor.rs index ef03979a52..3d34ff1fa9 100644 --- a/src/db/models/two_factor.rs +++ b/src/db/models/two_factor.rs @@ -1,19 +1,18 @@ use serde_json::Value; +use crate::db::schema::twofactor; use crate::{api::EmptyResult, db::DbConn, error::MapResult}; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = twofactor)] - #[diesel(primary_key(uuid))] - pub struct TwoFactor { - pub uuid: String, - pub user_uuid: String, - pub atype: i32, - pub enabled: bool, - pub data: String, - pub last_used: i32, - } +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = twofactor)] +#[diesel(primary_key(uuid))] +pub struct TwoFactor { + pub uuid: String, + pub user_uuid: String, + pub atype: i32, + pub enabled: bool, + pub data: String, + pub last_used: i32, } #[allow(dead_code)] @@ -72,7 +71,7 @@ impl TwoFactor { db_run! { conn: sqlite, mysql { match diesel::replace_into(twofactor::table) - .values(TwoFactorDb::to_db(self)) + .values(self) .execute(conn) { Ok(_) => Ok(()), @@ -80,7 +79,7 @@ impl TwoFactor { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(twofactor::table) .filter(twofactor::uuid.eq(&self.uuid)) - .set(TwoFactorDb::to_db(self)) + .set(self) .execute(conn) .map_res("Error saving twofactor") } @@ -88,7 +87,6 @@ impl TwoFactor { }.map_res("Error saving twofactor") } postgresql { - let value = TwoFactorDb::to_db(self); // We need to make sure we're not going to violate the unique constraint on user_uuid and atype. // This happens automatically on other DBMS backends due to replace_into(). PostgreSQL does // not support multiple constraints on ON CONFLICT clauses. @@ -97,10 +95,10 @@ impl TwoFactor { .map_res("Error deleting twofactor for insert")?; diesel::insert_into(twofactor::table) - .values(&value) + .values(self) .on_conflict(twofactor::uuid) .do_update() - .set(&value) + .set(self) .execute(conn) .map_res("Error saving twofactor") } @@ -120,9 +118,8 @@ impl TwoFactor { twofactor::table .filter(twofactor::user_uuid.eq(user_uuid)) .filter(twofactor::atype.lt(1000)) // Filter implementation types - .load::(conn) + .load::(conn) .expect("Error loading twofactor") - .from_db() }} } @@ -131,9 +128,8 @@ impl TwoFactor { twofactor::table .filter(twofactor::user_uuid.eq(user_uuid)) .filter(twofactor::atype.eq(atype)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -149,9 +145,8 @@ impl TwoFactor { let u2f_factors = db_run! { conn: { twofactor::table .filter(twofactor::atype.eq(TwoFactorType::U2f as i32)) - .load::(conn) + .load::(conn) .expect("Error loading twofactor") - .from_db() }}; use crate::api::core::two_factor::webauthn::U2FRegistration; diff --git a/src/db/models/two_factor_incomplete.rs b/src/db/models/two_factor_incomplete.rs index 49f7691f1a..6a2b8e5c2b 100644 --- a/src/db/models/two_factor_incomplete.rs +++ b/src/db/models/two_factor_incomplete.rs @@ -1,21 +1,20 @@ use chrono::{NaiveDateTime, Utc}; +use crate::db::schema::twofactor_incomplete; use crate::{api::EmptyResult, auth::ClientIp, db::DbConn, error::MapResult, CONFIG}; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = twofactor_incomplete)] - #[diesel(primary_key(user_uuid, device_uuid))] - pub struct TwoFactorIncomplete { - pub user_uuid: String, - // This device UUID is simply what's claimed by the device. It doesn't - // necessarily correspond to any UUID in the devices table, since a device - // must complete 2FA login before being added into the devices table. - pub device_uuid: String, - pub device_name: String, - pub login_time: NaiveDateTime, - pub ip_address: String, - } +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = twofactor_incomplete)] +#[diesel(primary_key(user_uuid, device_uuid))] +pub struct TwoFactorIncomplete { + pub user_uuid: String, + // This device UUID is simply what's claimed by the device. It doesn't + // necessarily correspond to any UUID in the devices table, since a device + // must complete 2FA login before being added into the devices table. + pub device_uuid: String, + pub device_name: String, + pub login_time: NaiveDateTime, + pub ip_address: String, } impl TwoFactorIncomplete { @@ -65,9 +64,8 @@ impl TwoFactorIncomplete { twofactor_incomplete::table .filter(twofactor_incomplete::user_uuid.eq(user_uuid)) .filter(twofactor_incomplete::device_uuid.eq(device_uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -75,9 +73,8 @@ impl TwoFactorIncomplete { db_run! {conn: { twofactor_incomplete::table .filter(twofactor_incomplete::login_time.lt(dt)) - .load::(conn) + .load::(conn) .expect("Error loading twofactor_incomplete") - .from_db() }} } diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 596d2d753d..cbf2792b01 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -4,62 +4,62 @@ use serde_json::Value; use crate::crypto; use crate::CONFIG; -db_object! { - #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[diesel(table_name = users)] - #[diesel(treat_none_as_null = true)] - #[diesel(primary_key(uuid))] - pub struct User { - pub uuid: String, - pub enabled: bool, - pub created_at: NaiveDateTime, - pub updated_at: NaiveDateTime, - pub verified_at: Option, - pub last_verifying_at: Option, - pub login_verify_count: i32, - - pub email: String, - pub email_new: Option, - pub email_new_token: Option, - pub name: String, - - pub password_hash: Vec, - pub salt: Vec, - pub password_iterations: i32, - pub password_hint: Option, - - pub akey: String, - pub private_key: Option, - pub public_key: Option, - - #[diesel(column_name = "totp_secret")] // Note, this is only added to the UserDb structs, not to User - _totp_secret: Option, - pub totp_recover: Option, - - pub security_stamp: String, - pub stamp_exception: Option, - - pub equivalent_domains: String, - pub excluded_globals: String, - - pub client_kdf_type: i32, - pub client_kdf_iter: i32, - pub client_kdf_memory: Option, - pub client_kdf_parallelism: Option, - - pub api_key: Option, - - pub avatar_color: Option, - - pub external_id: Option, - } +use crate::db::schema::{invitations, users}; + +#[derive(Identifiable, Queryable, Insertable, AsChangeset)] +#[diesel(table_name = users)] +#[diesel(treat_none_as_null = true)] +#[diesel(primary_key(uuid))] +pub struct User { + pub uuid: String, + pub enabled: bool, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub verified_at: Option, + pub last_verifying_at: Option, + pub login_verify_count: i32, + + pub email: String, + pub email_new: Option, + pub email_new_token: Option, + pub name: String, + + pub password_hash: Vec, + pub salt: Vec, + pub password_iterations: i32, + pub password_hint: Option, + + pub akey: String, + pub private_key: Option, + pub public_key: Option, + + #[diesel(column_name = "totp_secret")] // Note, this is only added to the UserDb structs, not to User + _totp_secret: Option, + pub totp_recover: Option, - #[derive(Identifiable, Queryable, Insertable)] - #[diesel(table_name = invitations)] - #[diesel(primary_key(email))] - pub struct Invitation { - pub email: String, - } + pub security_stamp: String, + pub stamp_exception: Option, + + pub equivalent_domains: String, + pub excluded_globals: String, + + pub client_kdf_type: i32, + pub client_kdf_iter: i32, + pub client_kdf_memory: Option, + pub client_kdf_parallelism: Option, + + pub api_key: Option, + + pub avatar_color: Option, + + pub external_id: Option, +} + +#[derive(Identifiable, Queryable, Insertable)] +#[diesel(table_name = invitations)] +#[diesel(primary_key(email))] +pub struct Invitation { + pub email: String, } pub enum UserKdfType { @@ -283,7 +283,7 @@ impl User { db_run! {conn: sqlite, mysql { match diesel::replace_into(users::table) - .values(UserDb::to_db(self)) + .values(&*self) .execute(conn) { Ok(_) => Ok(()), @@ -291,7 +291,7 @@ impl User { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(users::table) .filter(users::uuid.eq(&self.uuid)) - .set(UserDb::to_db(self)) + .set(&*self) .execute(conn) .map_res("Error saving user") } @@ -299,12 +299,11 @@ impl User { }.map_res("Error saving user") } postgresql { - let value = UserDb::to_db(self); diesel::insert_into(users::table) // Insert or update - .values(&value) + .values(&*self) .on_conflict(users::uuid) .do_update() - .set(&value) + .set(&*self) .execute(conn) .map_res("Error saving user") } @@ -380,27 +379,27 @@ impl User { db_run! {conn: { users::table .filter(users::email.eq(lower_mail)) - .first::(conn) + .first::(conn) .ok() - .from_db() + }} } pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! {conn: { - users::table.filter(users::uuid.eq(uuid)).first::(conn).ok().from_db() + users::table.filter(users::uuid.eq(uuid)).first::(conn).ok() }} } pub async fn find_by_external_id(id: &str, conn: &mut DbConn) -> Option { db_run! {conn: { - users::table.filter(users::external_id.eq(id)).first::(conn).ok().from_db() + users::table.filter(users::external_id.eq(id)).first::(conn).ok() }} } pub async fn get_all(conn: &mut DbConn) -> Vec { db_run! {conn: { - users::table.load::(conn).expect("Error loading users").from_db() + users::table.load::(conn).expect("Error loading users") }} } @@ -430,13 +429,13 @@ impl Invitation { // Not checking for ForeignKey Constraints here // Table invitations does not have any ForeignKey Constraints. diesel::replace_into(invitations::table) - .values(InvitationDb::to_db(self)) + .values(self) .execute(conn) .map_res("Error saving invitation") } postgresql { diesel::insert_into(invitations::table) - .values(InvitationDb::to_db(self)) + .values(self) .on_conflict(invitations::email) .do_nothing() .execute(conn) @@ -458,9 +457,9 @@ impl Invitation { db_run! {conn: { invitations::table .filter(invitations::email.eq(lower_mail)) - .first::(conn) + .first::(conn) .ok() - .from_db() + }} } diff --git a/src/db/schemas/mysql/schema.rs b/src/db/schemas/mysql/schema.rs deleted file mode 100644 index c2b2c9617c..0000000000 --- a/src/db/schemas/mysql/schema.rs +++ /dev/null @@ -1,360 +0,0 @@ -table! { - attachments (id) { - id -> Text, - cipher_uuid -> Text, - file_name -> Text, - file_size -> Integer, - akey -> Nullable, - } -} - -table! { - ciphers (uuid) { - uuid -> Text, - created_at -> Datetime, - updated_at -> Datetime, - user_uuid -> Nullable, - organization_uuid -> Nullable, - atype -> Integer, - name -> Text, - notes -> Nullable, - fields -> Nullable, - data -> Text, - password_history -> Nullable, - deleted_at -> Nullable, - reprompt -> Nullable, - } -} - -table! { - ciphers_collections (cipher_uuid, collection_uuid) { - cipher_uuid -> Text, - collection_uuid -> Text, - } -} - -table! { - collections (uuid) { - uuid -> Text, - org_uuid -> Text, - name -> Text, - external_id -> Nullable, - } -} - -table! { - devices (uuid, user_uuid) { - uuid -> Text, - created_at -> Datetime, - updated_at -> Datetime, - user_uuid -> Text, - name -> Text, - atype -> Integer, - push_uuid -> Nullable, - push_token -> Nullable, - refresh_token -> Text, - twofactor_remember -> Nullable, - } -} - -table! { - event (uuid) { - uuid -> Varchar, - event_type -> Integer, - user_uuid -> Nullable, - org_uuid -> Nullable, - cipher_uuid -> Nullable, - collection_uuid -> Nullable, - group_uuid -> Nullable, - org_user_uuid -> Nullable, - act_user_uuid -> Nullable, - device_type -> Nullable, - ip_address -> Nullable, - event_date -> Timestamp, - policy_uuid -> Nullable, - provider_uuid -> Nullable, - provider_user_uuid -> Nullable, - provider_org_uuid -> Nullable, - } -} - -table! { - favorites (user_uuid, cipher_uuid) { - user_uuid -> Text, - cipher_uuid -> Text, - } -} - -table! { - folders (uuid) { - uuid -> Text, - created_at -> Datetime, - updated_at -> Datetime, - user_uuid -> Text, - name -> Text, - } -} - -table! { - folders_ciphers (cipher_uuid, folder_uuid) { - cipher_uuid -> Text, - folder_uuid -> Text, - } -} - -table! { - invitations (email) { - email -> Text, - } -} - -table! { - org_policies (uuid) { - uuid -> Text, - org_uuid -> Text, - atype -> Integer, - enabled -> Bool, - data -> Text, - } -} - -table! { - organizations (uuid) { - uuid -> Text, - name -> Text, - billing_email -> Text, - private_key -> Nullable, - public_key -> Nullable, - } -} - -table! { - sends (uuid) { - uuid -> Text, - user_uuid -> Nullable, - organization_uuid -> Nullable, - name -> Text, - notes -> Nullable, - atype -> Integer, - data -> Text, - akey -> Text, - password_hash -> Nullable, - password_salt -> Nullable, - password_iter -> Nullable, - max_access_count -> Nullable, - access_count -> Integer, - creation_date -> Datetime, - revision_date -> Datetime, - expiration_date -> Nullable, - deletion_date -> Datetime, - disabled -> Bool, - hide_email -> Nullable, - } -} - -table! { - twofactor (uuid) { - uuid -> Text, - user_uuid -> Text, - atype -> Integer, - enabled -> Bool, - data -> Text, - last_used -> Integer, - } -} - -table! { - twofactor_incomplete (user_uuid, device_uuid) { - user_uuid -> Text, - device_uuid -> Text, - device_name -> Text, - login_time -> Timestamp, - ip_address -> Text, - } -} - -table! { - users (uuid) { - uuid -> Text, - enabled -> Bool, - created_at -> Datetime, - updated_at -> Datetime, - verified_at -> Nullable, - last_verifying_at -> Nullable, - login_verify_count -> Integer, - email -> Text, - email_new -> Nullable, - email_new_token -> Nullable, - name -> Text, - password_hash -> Binary, - salt -> Binary, - password_iterations -> Integer, - password_hint -> Nullable, - akey -> Text, - private_key -> Nullable, - public_key -> Nullable, - totp_secret -> Nullable, - totp_recover -> Nullable, - security_stamp -> Text, - stamp_exception -> Nullable, - equivalent_domains -> Text, - excluded_globals -> Text, - client_kdf_type -> Integer, - client_kdf_iter -> Integer, - client_kdf_memory -> Nullable, - client_kdf_parallelism -> Nullable, - api_key -> Nullable, - avatar_color -> Nullable, - external_id -> Nullable, - } -} - -table! { - users_collections (user_uuid, collection_uuid) { - user_uuid -> Text, - collection_uuid -> Text, - read_only -> Bool, - hide_passwords -> Bool, - } -} - -table! { - users_organizations (uuid) { - uuid -> Text, - user_uuid -> Text, - org_uuid -> Text, - access_all -> Bool, - akey -> Text, - status -> Integer, - atype -> Integer, - reset_password_key -> Nullable, - } -} - -table! { - organization_api_key (uuid, org_uuid) { - uuid -> Text, - org_uuid -> Text, - atype -> Integer, - api_key -> Text, - revision_date -> Timestamp, - } -} - -table! { - emergency_access (uuid) { - uuid -> Text, - grantor_uuid -> Text, - grantee_uuid -> Nullable, - email -> Nullable, - key_encrypted -> Nullable, - atype -> Integer, - status -> Integer, - wait_time_days -> Integer, - recovery_initiated_at -> Nullable, - last_notification_at -> Nullable, - updated_at -> Timestamp, - created_at -> Timestamp, - } -} - -table! { - groups (uuid) { - uuid -> Text, - organizations_uuid -> Text, - name -> Text, - access_all -> Bool, - external_id -> Nullable, - creation_date -> Timestamp, - revision_date -> Timestamp, - } -} - -table! { - groups_users (groups_uuid, users_organizations_uuid) { - groups_uuid -> Text, - users_organizations_uuid -> Text, - } -} - -table! { - collections_groups (collections_uuid, groups_uuid) { - collections_uuid -> Text, - groups_uuid -> Text, - read_only -> Bool, - hide_passwords -> Bool, - } -} - -table! { - auth_requests (uuid) { - uuid -> Text, - user_uuid -> Text, - organization_uuid -> Nullable, - request_device_identifier -> Text, - device_type -> Integer, - request_ip -> Text, - response_device_id -> Nullable, - access_code -> Text, - public_key -> Text, - enc_key -> Text, - master_password_hash -> Text, - approved -> Nullable, - creation_date -> Timestamp, - response_date -> Nullable, - authentication_date -> Nullable, - } -} - -joinable!(attachments -> ciphers (cipher_uuid)); -joinable!(ciphers -> organizations (organization_uuid)); -joinable!(ciphers -> users (user_uuid)); -joinable!(ciphers_collections -> ciphers (cipher_uuid)); -joinable!(ciphers_collections -> collections (collection_uuid)); -joinable!(collections -> organizations (org_uuid)); -joinable!(devices -> users (user_uuid)); -joinable!(folders -> users (user_uuid)); -joinable!(folders_ciphers -> ciphers (cipher_uuid)); -joinable!(folders_ciphers -> folders (folder_uuid)); -joinable!(org_policies -> organizations (org_uuid)); -joinable!(sends -> organizations (organization_uuid)); -joinable!(sends -> users (user_uuid)); -joinable!(twofactor -> users (user_uuid)); -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!(organization_api_key -> organizations (org_uuid)); -joinable!(emergency_access -> users (grantor_uuid)); -joinable!(groups -> organizations (organizations_uuid)); -joinable!(groups_users -> users_organizations (users_organizations_uuid)); -joinable!(groups_users -> groups (groups_uuid)); -joinable!(collections_groups -> collections (collections_uuid)); -joinable!(collections_groups -> groups (groups_uuid)); -joinable!(event -> users_organizations (uuid)); -joinable!(auth_requests -> users (user_uuid)); - -allow_tables_to_appear_in_same_query!( - attachments, - ciphers, - ciphers_collections, - collections, - devices, - folders, - folders_ciphers, - invitations, - org_policies, - organizations, - sends, - twofactor, - users, - users_collections, - users_organizations, - organization_api_key, - emergency_access, - groups, - groups_users, - collections_groups, - event, - auth_requests, -); diff --git a/src/db/schemas/postgresql/schema.rs b/src/db/schemas/postgresql/schema.rs deleted file mode 100644 index 4ae9e82156..0000000000 --- a/src/db/schemas/postgresql/schema.rs +++ /dev/null @@ -1,360 +0,0 @@ -table! { - attachments (id) { - id -> Text, - cipher_uuid -> Text, - file_name -> Text, - file_size -> Integer, - akey -> Nullable, - } -} - -table! { - ciphers (uuid) { - uuid -> Text, - created_at -> Timestamp, - updated_at -> Timestamp, - user_uuid -> Nullable, - organization_uuid -> Nullable, - atype -> Integer, - name -> Text, - notes -> Nullable, - fields -> Nullable, - data -> Text, - password_history -> Nullable, - deleted_at -> Nullable, - reprompt -> Nullable, - } -} - -table! { - ciphers_collections (cipher_uuid, collection_uuid) { - cipher_uuid -> Text, - collection_uuid -> Text, - } -} - -table! { - collections (uuid) { - uuid -> Text, - org_uuid -> Text, - name -> Text, - external_id -> Nullable, - } -} - -table! { - devices (uuid, user_uuid) { - uuid -> Text, - created_at -> Timestamp, - updated_at -> Timestamp, - user_uuid -> Text, - name -> Text, - atype -> Integer, - push_uuid -> Nullable, - push_token -> Nullable, - refresh_token -> Text, - twofactor_remember -> Nullable, - } -} - -table! { - event (uuid) { - uuid -> Text, - event_type -> Integer, - user_uuid -> Nullable, - org_uuid -> Nullable, - cipher_uuid -> Nullable, - collection_uuid -> Nullable, - group_uuid -> Nullable, - org_user_uuid -> Nullable, - act_user_uuid -> Nullable, - device_type -> Nullable, - ip_address -> Nullable, - event_date -> Timestamp, - policy_uuid -> Nullable, - provider_uuid -> Nullable, - provider_user_uuid -> Nullable, - provider_org_uuid -> Nullable, - } -} - -table! { - favorites (user_uuid, cipher_uuid) { - user_uuid -> Text, - cipher_uuid -> Text, - } -} - -table! { - folders (uuid) { - uuid -> Text, - created_at -> Timestamp, - updated_at -> Timestamp, - user_uuid -> Text, - name -> Text, - } -} - -table! { - folders_ciphers (cipher_uuid, folder_uuid) { - cipher_uuid -> Text, - folder_uuid -> Text, - } -} - -table! { - invitations (email) { - email -> Text, - } -} - -table! { - org_policies (uuid) { - uuid -> Text, - org_uuid -> Text, - atype -> Integer, - enabled -> Bool, - data -> Text, - } -} - -table! { - organizations (uuid) { - uuid -> Text, - name -> Text, - billing_email -> Text, - private_key -> Nullable, - public_key -> Nullable, - } -} - -table! { - sends (uuid) { - uuid -> Text, - user_uuid -> Nullable, - organization_uuid -> Nullable, - name -> Text, - notes -> Nullable, - atype -> Integer, - data -> Text, - akey -> Text, - password_hash -> Nullable, - password_salt -> Nullable, - password_iter -> Nullable, - max_access_count -> Nullable, - access_count -> Integer, - creation_date -> Timestamp, - revision_date -> Timestamp, - expiration_date -> Nullable, - deletion_date -> Timestamp, - disabled -> Bool, - hide_email -> Nullable, - } -} - -table! { - twofactor (uuid) { - uuid -> Text, - user_uuid -> Text, - atype -> Integer, - enabled -> Bool, - data -> Text, - last_used -> Integer, - } -} - -table! { - twofactor_incomplete (user_uuid, device_uuid) { - user_uuid -> Text, - device_uuid -> Text, - device_name -> Text, - login_time -> Timestamp, - ip_address -> Text, - } -} - -table! { - users (uuid) { - uuid -> Text, - enabled -> Bool, - created_at -> Timestamp, - updated_at -> Timestamp, - verified_at -> Nullable, - last_verifying_at -> Nullable, - login_verify_count -> Integer, - email -> Text, - email_new -> Nullable, - email_new_token -> Nullable, - name -> Text, - password_hash -> Binary, - salt -> Binary, - password_iterations -> Integer, - password_hint -> Nullable, - akey -> Text, - private_key -> Nullable, - public_key -> Nullable, - totp_secret -> Nullable, - totp_recover -> Nullable, - security_stamp -> Text, - stamp_exception -> Nullable, - equivalent_domains -> Text, - excluded_globals -> Text, - client_kdf_type -> Integer, - client_kdf_iter -> Integer, - client_kdf_memory -> Nullable, - client_kdf_parallelism -> Nullable, - api_key -> Nullable, - avatar_color -> Nullable, - external_id -> Nullable, - } -} - -table! { - users_collections (user_uuid, collection_uuid) { - user_uuid -> Text, - collection_uuid -> Text, - read_only -> Bool, - hide_passwords -> Bool, - } -} - -table! { - users_organizations (uuid) { - uuid -> Text, - user_uuid -> Text, - org_uuid -> Text, - access_all -> Bool, - akey -> Text, - status -> Integer, - atype -> Integer, - reset_password_key -> Nullable, - } -} - -table! { - organization_api_key (uuid, org_uuid) { - uuid -> Text, - org_uuid -> Text, - atype -> Integer, - api_key -> Text, - revision_date -> Timestamp, - } -} - -table! { - emergency_access (uuid) { - uuid -> Text, - grantor_uuid -> Text, - grantee_uuid -> Nullable, - email -> Nullable, - key_encrypted -> Nullable, - atype -> Integer, - status -> Integer, - wait_time_days -> Integer, - recovery_initiated_at -> Nullable, - last_notification_at -> Nullable, - updated_at -> Timestamp, - created_at -> Timestamp, - } -} - -table! { - groups (uuid) { - uuid -> Text, - organizations_uuid -> Text, - name -> Text, - access_all -> Bool, - external_id -> Nullable, - creation_date -> Timestamp, - revision_date -> Timestamp, - } -} - -table! { - groups_users (groups_uuid, users_organizations_uuid) { - groups_uuid -> Text, - users_organizations_uuid -> Text, - } -} - -table! { - collections_groups (collections_uuid, groups_uuid) { - collections_uuid -> Text, - groups_uuid -> Text, - read_only -> Bool, - hide_passwords -> Bool, - } -} - -table! { - auth_requests (uuid) { - uuid -> Text, - user_uuid -> Text, - organization_uuid -> Nullable, - request_device_identifier -> Text, - device_type -> Integer, - request_ip -> Text, - response_device_id -> Nullable, - access_code -> Text, - public_key -> Text, - enc_key -> Text, - master_password_hash -> Text, - approved -> Nullable, - creation_date -> Timestamp, - response_date -> Nullable, - authentication_date -> Nullable, - } -} - -joinable!(attachments -> ciphers (cipher_uuid)); -joinable!(ciphers -> organizations (organization_uuid)); -joinable!(ciphers -> users (user_uuid)); -joinable!(ciphers_collections -> ciphers (cipher_uuid)); -joinable!(ciphers_collections -> collections (collection_uuid)); -joinable!(collections -> organizations (org_uuid)); -joinable!(devices -> users (user_uuid)); -joinable!(folders -> users (user_uuid)); -joinable!(folders_ciphers -> ciphers (cipher_uuid)); -joinable!(folders_ciphers -> folders (folder_uuid)); -joinable!(org_policies -> organizations (org_uuid)); -joinable!(sends -> organizations (organization_uuid)); -joinable!(sends -> users (user_uuid)); -joinable!(twofactor -> users (user_uuid)); -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!(organization_api_key -> organizations (org_uuid)); -joinable!(emergency_access -> users (grantor_uuid)); -joinable!(groups -> organizations (organizations_uuid)); -joinable!(groups_users -> users_organizations (users_organizations_uuid)); -joinable!(groups_users -> groups (groups_uuid)); -joinable!(collections_groups -> collections (collections_uuid)); -joinable!(collections_groups -> groups (groups_uuid)); -joinable!(event -> users_organizations (uuid)); -joinable!(auth_requests -> users (user_uuid)); - -allow_tables_to_appear_in_same_query!( - attachments, - ciphers, - ciphers_collections, - collections, - devices, - folders, - folders_ciphers, - invitations, - org_policies, - organizations, - sends, - twofactor, - users, - users_collections, - users_organizations, - organization_api_key, - emergency_access, - groups, - groups_users, - collections_groups, - event, - auth_requests, -); diff --git a/src/db/schemas/sqlite/schema.rs b/src/db/schemas/schema.rs similarity index 100% rename from src/db/schemas/sqlite/schema.rs rename to src/db/schemas/schema.rs