From 1b1596ce1e1534020a9a7735ae781504da48cda5 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Thu, 24 Aug 2023 22:06:32 +0200 Subject: [PATCH] Use Diesels MultiConnections Derive With this PR we remove allmost all custom macro's to create the multiple database type code. This is now handled by Diesel it self. This removed the need of the following functions/macro's: - `db_object!` - `::to_db` - `.from_db()` All `conn` variables do not need a `mut` anymore (says nightly rust) It is also possible to just use one schema instead of multiple per type. --- Cargo.lock | 11 - Cargo.toml | 10 +- src/api/admin.rs | 164 ++++--- src/api/core/accounts.rs | 175 ++++--- src/api/core/ciphers.rs | 272 +++++----- src/api/core/emergency_access.rs | 172 ++++--- src/api/core/events.rs | 36 +- src/api/core/folders.rs | 30 +- src/api/core/mod.rs | 4 +- src/api/core/organizations.rs | 573 ++++++++++------------ src/api/core/public.rs | 42 +- src/api/core/sends.rs | 126 +++-- src/api/core/two_factor/authenticator.rs | 16 +- src/api/core/two_factor/duo.rs | 20 +- src/api/core/two_factor/email.rs | 32 +- src/api/core/two_factor/mod.rs | 40 +- src/api/core/two_factor/webauthn.rs | 49 +- src/api/core/two_factor/yubikey.rs | 14 +- src/api/identity.rs | 41 +- src/api/notifications.rs | 10 +- src/api/push.rs | 25 +- src/auth.rs | 20 +- src/config.rs | 19 +- src/db/mod.rs | 469 +++++++----------- src/db/models/attachment.rs | 61 ++- src/db/models/auth_request.rs | 71 ++- src/db/models/cipher.rs | 137 +++--- src/db/models/collection.rs | 163 +++--- src/db/models/device.rs | 76 ++- src/db/models/emergency_access.rs | 156 +++--- src/db/models/event.rs | 134 +++-- src/db/models/favorite.rs | 32 +- src/db/models/folder.rs | 84 ++-- src/db/models/group.rs | 142 +++--- src/db/models/org_policy.rs | 76 ++- src/db/models/organization.rs | 217 ++++---- src/db/models/send.rs | 94 ++-- src/db/models/two_factor.rs | 53 +- src/db/models/two_factor_incomplete.rs | 47 +- src/db/models/user.rs | 145 +++--- src/db/schemas/mysql/schema.rs | 362 -------------- src/db/schemas/{postgresql => }/schema.rs | 0 src/db/schemas/sqlite/schema.rs | 362 -------------- src/main.rs | 2 +- 44 files changed, 1883 insertions(+), 2901 deletions(-) delete mode 100644 src/db/schemas/mysql/schema.rs rename src/db/schemas/{postgresql => }/schema.rs (100%) delete mode 100644 src/db/schemas/sqlite/schema.rs diff --git a/Cargo.lock b/Cargo.lock index ddeabdd9..e157d537 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -722,16 +722,6 @@ dependencies = [ "syn 2.0.29", ] -[[package]] -name = "diesel_logger" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23010b507517129dc9b11fb35f36d76fd2d3dd4c85232733697622e345375f2f" -dependencies = [ - "diesel", - "log", -] - [[package]] name = "diesel_migrations" version = "2.1.0" @@ -3469,7 +3459,6 @@ dependencies = [ "data-encoding", "data-url", "diesel", - "diesel_logger", "diesel_migrations", "dotenvy", "email_address", diff --git a/Cargo.toml b/Cargo.toml index ee3e789c..c5d980f2 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"] @@ -28,7 +32,7 @@ enable_mimalloc = ["mimalloc"] # It enables the usage of the diesel_logger crate, which is able to output the generated queries. # You also need to set an env variable `QUERY_LOGGER=1` to fully activate this so you do not have to re-compile # if you want to turn off the logging for a specific run. -query_logger = ["diesel_logger"] +# query_logger = ["diesel_logger"] # Currently not able to be used with MultiConnection. # Enable unstable features, requires nightly # Currently only used to enable rusts official ip support @@ -77,7 +81,7 @@ serde_json = "1.0.105" # A safe, extensible ORM and Query builder diesel = { version = "2.1.1", features = ["chrono", "r2d2"] } diesel_migrations = "2.1.0" -diesel_logger = { version = "0.3.0", optional = true } +# diesel_logger = { version = "0.3.0", optional = true } # Currently not able to be used with MultiConnection. # Bundled/Static SQLite libsqlite3-sys = { version = "0.26.0", features = ["bundled"], optional = true } diff --git a/src/api/admin.rs b/src/api/admin.rs index 56f3cd77..7edd4083 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 { @@ -268,7 +275,7 @@ struct InviteData { email: String, } -async fn get_user_or_404(uuid: &str, conn: &mut DbConn) -> ApiResult { +async fn get_user_or_404(uuid: &str, conn: &DbConn) -> ApiResult { if let Some(user) = User::find_by_uuid(uuid, conn).await { Ok(user) } else { @@ -277,15 +284,15 @@ async fn get_user_or_404(uuid: &str, conn: &mut DbConn) -> ApiResult { } #[post("/invite", data = "")] -async fn invite_user(data: Json, _token: AdminToken, mut conn: DbConn) -> JsonResult { +async fn invite_user(data: Json, _token: AdminToken, conn: DbConn) -> JsonResult { let data: InviteData = data.into_inner(); - if User::find_by_mail(&data.email, &mut conn).await.is_some() { + if User::find_by_mail(&data.email, &conn).await.is_some() { err_code!("User already exists", Status::Conflict.code) } let mut user = User::new(data.email); - async fn _generate_invite(user: &User, conn: &mut DbConn) -> EmptyResult { + async fn _generate_invite(user: &User, conn: &DbConn) -> EmptyResult { if CONFIG.mail_enabled() { mail::send_invite(&user.email, &user.uuid, None, None, &CONFIG.invitation_org_name(), None).await } else { @@ -294,10 +301,10 @@ async fn invite_user(data: Json, _token: AdminToken, mut conn: DbCon } } - _generate_invite(&user, &mut conn).await.map_err(|e| e.with_code(Status::InternalServerError.code))?; - user.save(&mut conn).await.map_err(|e| e.with_code(Status::InternalServerError.code))?; + _generate_invite(&user, &conn).await.map_err(|e| e.with_code(Status::InternalServerError.code))?; + user.save(&conn).await.map_err(|e| e.with_code(Status::InternalServerError.code))?; - Ok(Json(user.to_json(&mut conn).await)) + Ok(Json(user.to_json(&conn).await)) } #[post("/test/smtp", data = "")] @@ -318,14 +325,14 @@ fn logout(cookies: &CookieJar<'_>) -> Redirect { } #[get("/users")] -async fn get_users_json(_token: AdminToken, mut conn: DbConn) -> Json { - let users = User::get_all(&mut conn).await; +async fn get_users_json(_token: AdminToken, conn: DbConn) -> Json { + let users = User::get_all(&conn).await; let mut users_json = Vec::with_capacity(users.len()); for u in users { - let mut usr = u.to_json(&mut conn).await; + let mut usr = u.to_json(&conn).await; usr["UserEnabled"] = json!(u.enabled); usr["CreatedAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); - usr["LastActive"] = match u.last_active(&mut conn).await { + usr["LastActive"] = match u.last_active(&conn).await { Some(dt) => json!(format_naive_datetime_local(&dt, DT_FMT)), None => json!(None::), }; @@ -336,17 +343,17 @@ async fn get_users_json(_token: AdminToken, mut conn: DbConn) -> Json { } #[get("/users/overview")] -async fn users_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult> { - let users = User::get_all(&mut conn).await; +async fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult> { + let users = User::get_all(&conn).await; let mut users_json = Vec::with_capacity(users.len()); for u in users { - let mut usr = u.to_json(&mut conn).await; - usr["cipher_count"] = json!(Cipher::count_owned_by_user(&u.uuid, &mut conn).await); - usr["attachment_count"] = json!(Attachment::count_by_user(&u.uuid, &mut conn).await); - usr["attachment_size"] = json!(get_display_size(Attachment::size_by_user(&u.uuid, &mut conn).await as i32)); + let mut usr = u.to_json(&conn).await; + usr["cipher_count"] = json!(Cipher::count_owned_by_user(&u.uuid, &conn).await); + usr["attachment_count"] = json!(Attachment::count_by_user(&u.uuid, &conn).await); + usr["attachment_size"] = json!(get_display_size(Attachment::size_by_user(&u.uuid, &conn).await as i32)); usr["user_enabled"] = json!(u.enabled); usr["created_at"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); - usr["last_active"] = match u.last_active(&mut conn).await { + usr["last_active"] = match u.last_active(&conn).await { Some(dt) => json!(format_naive_datetime_local(&dt, DT_FMT)), None => json!("Never"), }; @@ -358,9 +365,9 @@ async fn users_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult")] -async fn get_user_by_mail_json(mail: &str, _token: AdminToken, mut conn: DbConn) -> JsonResult { - if let Some(u) = User::find_by_mail(mail, &mut conn).await { - let mut usr = u.to_json(&mut conn).await; +async fn get_user_by_mail_json(mail: &str, _token: AdminToken, conn: DbConn) -> JsonResult { + if let Some(u) = User::find_by_mail(mail, &conn).await { + let mut usr = u.to_json(&conn).await; usr["UserEnabled"] = json!(u.enabled); usr["CreatedAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); Ok(Json(usr)) @@ -370,21 +377,21 @@ async fn get_user_by_mail_json(mail: &str, _token: AdminToken, mut conn: DbConn) } #[get("/users/")] -async fn get_user_json(uuid: &str, _token: AdminToken, mut conn: DbConn) -> JsonResult { - let u = get_user_or_404(uuid, &mut conn).await?; - let mut usr = u.to_json(&mut conn).await; +async fn get_user_json(uuid: &str, _token: AdminToken, conn: DbConn) -> JsonResult { + let u = get_user_or_404(uuid, &conn).await?; + let mut usr = u.to_json(&conn).await; usr["UserEnabled"] = json!(u.enabled); usr["CreatedAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); Ok(Json(usr)) } #[post("/users//delete")] -async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyResult { - let user = get_user_or_404(uuid, &mut conn).await?; +async fn delete_user(uuid: &str, token: AdminToken, conn: DbConn) -> EmptyResult { + let user = get_user_or_404(uuid, &conn).await?; // Get the user_org records before deleting the actual user - let user_orgs = UserOrganization::find_any_state_by_user(uuid, &mut conn).await; - let res = user.delete(&mut conn).await; + let user_orgs = UserOrganization::find_any_state_by_user(uuid, &conn).await; + let res = user.delete(&conn).await; for user_org in user_orgs { log_event( @@ -394,7 +401,7 @@ async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyRe String::from(ACTING_ADMIN_USER), 14, // Use UnknownBrowser type &token.ip.ip, - &mut conn, + &conn, ) .await; } @@ -403,13 +410,13 @@ async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyRe } #[post("/users//deauth")] -async fn deauth_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - let mut user = get_user_or_404(uuid, &mut conn).await?; +async fn deauth_user(uuid: &str, _token: AdminToken, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + let mut user = get_user_or_404(uuid, &conn).await?; nt.send_logout(&user, None).await; if CONFIG.push_enabled() { - for device in Device::find_push_devices_by_user(&user.uuid, &mut conn).await { + for device in Device::find_push_devices_by_user(&user.uuid, &conn).await { match unregister_push_device(device.uuid).await { Ok(r) => r, Err(e) => error!("Unable to unregister devices from Bitwarden server: {}", e), @@ -417,20 +424,20 @@ async fn deauth_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notif } } - Device::delete_all_by_user(&user.uuid, &mut conn).await?; + Device::delete_all_by_user(&user.uuid, &conn).await?; user.reset_security_stamp(); - user.save(&mut conn).await + user.save(&conn).await } #[post("/users//disable")] -async fn disable_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - let mut user = get_user_or_404(uuid, &mut conn).await?; - Device::delete_all_by_user(&user.uuid, &mut conn).await?; +async fn disable_user(uuid: &str, _token: AdminToken, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + let mut user = get_user_or_404(uuid, &conn).await?; + Device::delete_all_by_user(&user.uuid, &conn).await?; user.reset_security_stamp(); user.enabled = false; - let save_result = user.save(&mut conn).await; + let save_result = user.save(&conn).await; nt.send_logout(&user, None).await; @@ -438,24 +445,24 @@ async fn disable_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Noti } #[post("/users//enable")] -async fn enable_user(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyResult { - let mut user = get_user_or_404(uuid, &mut conn).await?; +async fn enable_user(uuid: &str, _token: AdminToken, conn: DbConn) -> EmptyResult { + let mut user = get_user_or_404(uuid, &conn).await?; user.enabled = true; - user.save(&mut conn).await + user.save(&conn).await } #[post("/users//remove-2fa")] -async fn remove_2fa(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyResult { - let mut user = get_user_or_404(uuid, &mut conn).await?; - TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?; +async fn remove_2fa(uuid: &str, _token: AdminToken, conn: DbConn) -> EmptyResult { + let mut user = get_user_or_404(uuid, &conn).await?; + TwoFactor::delete_all_by_user(&user.uuid, &conn).await?; user.totp_recover = None; - user.save(&mut conn).await + user.save(&conn).await } #[post("/users//invite/resend")] -async fn resend_user_invite(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyResult { - if let Some(user) = User::find_by_uuid(uuid, &mut conn).await { +async fn resend_user_invite(uuid: &str, _token: AdminToken, conn: DbConn) -> EmptyResult { + if let Some(user) = User::find_by_uuid(uuid, &conn).await { //TODO: replace this with user.status check when it will be available (PR#3397) if !user.password_hash.is_empty() { err_code!("User already accepted invitation", Status::BadRequest.code); @@ -479,14 +486,13 @@ struct UserOrgTypeData { } #[post("/users/org_type", data = "")] -async fn update_user_org_type(data: Json, token: AdminToken, mut conn: DbConn) -> EmptyResult { +async fn update_user_org_type(data: Json, token: AdminToken, conn: DbConn) -> EmptyResult { let data: UserOrgTypeData = data.into_inner(); - let mut user_to_edit = - match UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await { - Some(user) => user, - None => err!("The specified user isn't member of the organization"), - }; + let mut user_to_edit = match UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &conn).await { + Some(user) => user, + None => err!("The specified user isn't member of the organization"), + }; let new_type = match UserOrgType::from_str(&data.user_type.into_string()) { Some(new_type) => new_type as i32, @@ -495,7 +501,7 @@ async fn update_user_org_type(data: Json, token: AdminToken, mu if user_to_edit.atype == UserOrgType::Owner && new_type != UserOrgType::Owner { // Removing owner permission, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(&data.org_uuid, UserOrgType::Owner, &mut conn).await <= 1 { + if UserOrganization::count_confirmed_by_org_and_type(&data.org_uuid, UserOrgType::Owner, &conn).await <= 1 { err!("Can't change the type of the last owner") } } @@ -503,7 +509,7 @@ async fn update_user_org_type(data: Json, token: AdminToken, mu // This check is also done at api::organizations::{accept_invite(), _confirm_invite, _activate_user(), edit_user()}, update_user_org_type // It returns different error messages per function. if new_type < UserOrgType::Admin { - match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, &user_to_edit.org_uuid, true, &mut conn).await { + match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, &user_to_edit.org_uuid, true, &conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { err!("You cannot modify this user to this type because it has no two-step login method activated"); @@ -521,32 +527,32 @@ async fn update_user_org_type(data: Json, token: AdminToken, mu String::from(ACTING_ADMIN_USER), 14, // Use UnknownBrowser type &token.ip.ip, - &mut conn, + &conn, ) .await; user_to_edit.atype = new_type; - user_to_edit.save(&mut conn).await + user_to_edit.save(&conn).await } #[post("/users/update_revision")] -async fn update_revision_users(_token: AdminToken, mut conn: DbConn) -> EmptyResult { - User::update_all_revisions(&mut conn).await +async fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult { + User::update_all_revisions(&conn).await } #[get("/organizations/overview")] -async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult> { - let organizations = Organization::get_all(&mut conn).await; +async fn organizations_overview(_token: AdminToken, conn: DbConn) -> ApiResult> { + let organizations = Organization::get_all(&conn).await; let mut organizations_json = Vec::with_capacity(organizations.len()); for o in organizations { let mut org = o.to_json(); - org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &mut conn).await); - org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &mut conn).await); - org["collection_count"] = json!(Collection::count_by_org(&o.uuid, &mut conn).await); - org["group_count"] = json!(Group::count_by_org(&o.uuid, &mut conn).await); - org["event_count"] = json!(Event::count_by_org(&o.uuid, &mut conn).await); - org["attachment_count"] = json!(Attachment::count_by_org(&o.uuid, &mut conn).await); - org["attachment_size"] = json!(get_display_size(Attachment::size_by_org(&o.uuid, &mut conn).await as i32)); + org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &conn).await); + org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &conn).await); + org["collection_count"] = json!(Collection::count_by_org(&o.uuid, &conn).await); + org["group_count"] = json!(Group::count_by_org(&o.uuid, &conn).await); + org["event_count"] = json!(Event::count_by_org(&o.uuid, &conn).await); + org["attachment_count"] = json!(Attachment::count_by_org(&o.uuid, &conn).await); + org["attachment_size"] = json!(get_display_size(Attachment::size_by_org(&o.uuid, &conn).await as i32)); organizations_json.push(org); } @@ -555,9 +561,9 @@ async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResu } #[post("/organizations//delete")] -async fn delete_organization(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyResult { - let org = Organization::find_by_uuid(uuid, &mut conn).await.map_res("Organization doesn't exist")?; - org.delete(&mut conn).await +async fn delete_organization(uuid: &str, _token: AdminToken, conn: DbConn) -> EmptyResult { + let org = Organization::find_by_uuid(uuid, &conn).await.map_res("Organization doesn't exist")?; + org.delete(&conn).await } #[derive(Deserialize)] @@ -660,7 +666,7 @@ async fn get_ntp_time(has_http_access: bool) -> String { } #[get("/diagnostics")] -async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn) -> ApiResult> { +async fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResult> { use chrono::prelude::*; use std::net::ToSocketAddrs; @@ -715,7 +721,7 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn) "ip_header_config": &CONFIG.ip_header(), "uses_proxy": uses_proxy, "db_type": *DB_TYPE, - "db_version": get_sql_server_version(&mut conn).await, + "db_version": get_sql_server_version(&conn).await, "admin_url": format!("{}/diagnostics", admin_url()), "overrides": &CONFIG.get_overrides().join(", "), "host_arch": std::env::consts::ARCH, @@ -747,9 +753,9 @@ fn delete_config(_token: AdminToken) -> EmptyResult { } #[post("/config/backup_db")] -async fn backup_db(_token: AdminToken, mut conn: DbConn) -> EmptyResult { +async fn backup_db(_token: AdminToken, conn: DbConn) -> EmptyResult { if *CAN_BACKUP { - backup_database(&mut conn).await + backup_database(&conn).await } else { err!("Can't back up current DB (Only SQLite supports this feature)"); } diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 6f6e2f3d..8b47b9b4 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -108,7 +108,7 @@ async fn register(data: JsonUpcase, conn: DbConn) -> JsonResult { _register(data, conn).await } -pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> JsonResult { +pub async fn _register(data: JsonUpcase, conn: DbConn) -> JsonResult { let data: RegisterData = data.into_inner().data; let email = data.Email.to_lowercase(); @@ -127,7 +127,7 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json let mut verified_by_invite = false; - let mut user = match User::find_by_mail(&email, &mut conn).await { + let mut user = match User::find_by_mail(&email, &conn).await { Some(mut user) => { if !user.password_hash.is_empty() { err!("Registration not allowed or user already exists") @@ -143,14 +143,14 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json } else { err!("Registration email does not match invite email") } - } else if Invitation::take(&email, &mut conn).await { - for user_org in UserOrganization::find_invited_by_user(&user.uuid, &mut conn).await.iter_mut() { + } else if Invitation::take(&email, &conn).await { + for user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).await.iter_mut() { user_org.status = UserOrgStatus::Accepted as i32; - user_org.save(&mut conn).await?; + user_org.save(&conn).await?; } user } else if CONFIG.is_signup_allowed(&email) - || EmergencyAccess::find_invited_by_grantee_email(&email, &mut conn).await.is_some() + || EmergencyAccess::find_invited_by_grantee_email(&email, &conn).await.is_some() { user } else { @@ -161,7 +161,7 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json // Order is important here; the invitation check must come first // because the vaultwarden admin can invite anyone, regardless // of other signup restrictions. - if Invitation::take(&email, &mut conn).await || CONFIG.is_signup_allowed(&email) { + if Invitation::take(&email, &conn).await || CONFIG.is_signup_allowed(&email) { User::new(email.clone()) } else { err!("Registration not allowed or user already exists") @@ -170,7 +170,7 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json }; // Make sure we don't leave a lingering invitation. - Invitation::take(&email, &mut conn).await; + Invitation::take(&email, &conn).await; if let Some(client_kdf_type) = data.Kdf { user.client_kdf_type = client_kdf_type; @@ -208,7 +208,7 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json } } - user.save(&mut conn).await?; + user.save(&conn).await?; Ok(Json(json!({ "Object": "register", "CaptchaBypassToken": "", @@ -216,8 +216,8 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json } #[get("/accounts/profile")] -async fn profile(headers: Headers, mut conn: DbConn) -> Json { - Json(headers.user.to_json(&mut conn).await) +async fn profile(headers: Headers, conn: DbConn) -> Json { + Json(headers.user.to_json(&conn).await) } #[derive(Deserialize, Debug)] @@ -234,7 +234,7 @@ async fn put_profile(data: JsonUpcase, headers: Headers, conn: DbCo } #[post("/accounts/profile", data = "")] -async fn post_profile(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn post_profile(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: ProfileData = data.into_inner().data; // Check if the length of the username exceeds 50 characters (Same is Upstream Bitwarden) @@ -246,8 +246,8 @@ async fn post_profile(data: JsonUpcase, headers: Headers, mut conn: let mut user = headers.user; user.name = data.Name; - user.save(&mut conn).await?; - Ok(Json(user.to_json(&mut conn).await)) + user.save(&conn).await?; + Ok(Json(user.to_json(&conn).await)) } #[derive(Deserialize)] @@ -257,7 +257,7 @@ struct AvatarData { } #[put("/accounts/avatar", data = "")] -async fn put_avatar(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn put_avatar(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: AvatarData = data.into_inner().data; // It looks like it only supports the 6 hex color format. @@ -272,13 +272,13 @@ async fn put_avatar(data: JsonUpcase, headers: Headers, mut conn: Db let mut user = headers.user; user.avatar_color = data.AvatarColor; - user.save(&mut conn).await?; - Ok(Json(user.to_json(&mut conn).await)) + user.save(&conn).await?; + Ok(Json(user.to_json(&conn).await)) } #[get("/users//public-key")] -async fn get_public_keys(uuid: &str, _headers: Headers, mut conn: DbConn) -> JsonResult { - let user = match User::find_by_uuid(uuid, &mut conn).await { +async fn get_public_keys(uuid: &str, _headers: Headers, conn: DbConn) -> JsonResult { + let user = match User::find_by_uuid(uuid, &conn).await { Some(user) => user, None => err!("User doesn't exist"), }; @@ -291,7 +291,7 @@ async fn get_public_keys(uuid: &str, _headers: Headers, mut conn: DbConn) -> Jso } #[post("/accounts/keys", data = "")] -async fn post_keys(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn post_keys(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: KeysData = data.into_inner().data; let mut user = headers.user; @@ -299,7 +299,7 @@ async fn post_keys(data: JsonUpcase, headers: Headers, mut conn: DbCon user.private_key = Some(data.EncryptedPrivateKey); user.public_key = Some(data.PublicKey); - user.save(&mut conn).await?; + user.save(&conn).await?; Ok(Json(json!({ "PrivateKey": user.private_key, @@ -321,7 +321,7 @@ struct ChangePassData { async fn post_password( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let data: ChangePassData = data.into_inner().data; @@ -334,7 +334,7 @@ async fn post_password( user.password_hint = clean_password_hint(&data.MasterPasswordHint); enforce_password_hint_setting(&user.password_hint)?; - log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn) + log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn) .await; user.set_password( @@ -344,7 +344,7 @@ async fn post_password( Some(vec![String::from("post_rotatekey"), String::from("get_contacts"), String::from("get_public_keys")]), ); - let save_result = user.save(&mut conn).await; + let save_result = user.save(&conn).await; // Prevent logging out the client where the user requested this endpoint from. // If you do logout the user it will causes issues at the client side. @@ -368,7 +368,7 @@ struct ChangeKdfData { } #[post("/accounts/kdf", data = "")] -async fn post_kdf(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { +async fn post_kdf(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { let data: ChangeKdfData = data.into_inner().data; let mut user = headers.user; @@ -407,7 +407,7 @@ async fn post_kdf(data: JsonUpcase, headers: Headers, mut conn: D user.client_kdf_iter = data.KdfIterations; user.client_kdf_type = data.Kdf; user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None); - let save_result = user.save(&mut conn).await; + let save_result = user.save(&conn).await; nt.send_logout(&user, Some(headers.device.uuid)).await; @@ -434,7 +434,7 @@ struct KeyData { } #[post("/accounts/key", data = "")] -async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { +async fn post_rotatekey(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { let data: KeyData = data.into_inner().data; if !headers.user.check_valid_password(&data.MasterPasswordHash) { @@ -451,7 +451,7 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: D // Update folder data for folder_data in data.Folders { - let mut saved_folder = match Folder::find_by_uuid(&folder_data.Id, &mut conn).await { + let mut saved_folder = match Folder::find_by_uuid(&folder_data.Id, &conn).await { Some(folder) => folder, None => err!("Folder doesn't exist"), }; @@ -461,14 +461,14 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: D } saved_folder.name = folder_data.Name; - saved_folder.save(&mut conn).await? + saved_folder.save(&conn).await? } // Update cipher data use super::ciphers::update_cipher_from_data; for cipher_data in data.Ciphers { - let mut saved_cipher = match Cipher::find_by_uuid(cipher_data.Id.as_ref().unwrap(), &mut conn).await { + let mut saved_cipher = match Cipher::find_by_uuid(cipher_data.Id.as_ref().unwrap(), &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; @@ -480,8 +480,7 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: D // Prevent triggering cipher updates via WebSockets by settings UpdateType::None // The user sessions are invalidated because all the ciphers were re-encrypted and thus triggering an update could cause issues. // We force the users to logout after the user has been saved to try and prevent these issues. - update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None) - .await? + update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None).await? } // Update user data @@ -491,7 +490,7 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: D user.private_key = Some(data.PrivateKey); user.reset_security_stamp(); - let save_result = user.save(&mut conn).await; + let save_result = user.save(&conn).await; // Prevent logging out the client where the user requested this endpoint from. // If you do logout the user it will causes issues at the client side. @@ -502,12 +501,7 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: D } #[post("/accounts/security-stamp", data = "")] -async fn post_sstamp( - data: JsonUpcase, - headers: Headers, - mut conn: DbConn, - nt: Notify<'_>, -) -> EmptyResult { +async fn post_sstamp(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { let data: PasswordData = data.into_inner().data; let mut user = headers.user; @@ -515,9 +509,9 @@ async fn post_sstamp( err!("Invalid password") } - Device::delete_all_by_user(&user.uuid, &mut conn).await?; + Device::delete_all_by_user(&user.uuid, &conn).await?; user.reset_security_stamp(); - let save_result = user.save(&mut conn).await; + let save_result = user.save(&conn).await; nt.send_logout(&user, None).await; @@ -532,7 +526,7 @@ struct EmailTokenData { } #[post("/accounts/email-token", data = "")] -async fn post_email_token(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn post_email_token(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { if !CONFIG.email_change_allowed() { err!("Email change is not allowed."); } @@ -544,7 +538,7 @@ async fn post_email_token(data: JsonUpcase, headers: Headers, mu err!("Invalid password") } - if User::find_by_mail(&data.NewEmail, &mut conn).await.is_some() { + if User::find_by_mail(&data.NewEmail, &conn).await.is_some() { err!("Email already in use"); } @@ -562,7 +556,7 @@ async fn post_email_token(data: JsonUpcase, headers: Headers, mu user.email_new = Some(data.NewEmail); user.email_new_token = Some(token); - user.save(&mut conn).await + user.save(&conn).await } #[derive(Deserialize)] @@ -577,12 +571,7 @@ struct ChangeEmailData { } #[post("/accounts/email", data = "")] -async fn post_email( - data: JsonUpcase, - headers: Headers, - mut conn: DbConn, - nt: Notify<'_>, -) -> EmptyResult { +async fn post_email(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { if !CONFIG.email_change_allowed() { err!("Email change is not allowed."); } @@ -594,7 +583,7 @@ async fn post_email( err!("Invalid password") } - if User::find_by_mail(&data.NewEmail, &mut conn).await.is_some() { + if User::find_by_mail(&data.NewEmail, &conn).await.is_some() { err!("Email already in use"); } @@ -628,7 +617,7 @@ async fn post_email( user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None); - let save_result = user.save(&mut conn).await; + let save_result = user.save(&conn).await; nt.send_logout(&user, None).await; @@ -658,10 +647,10 @@ struct VerifyEmailTokenData { } #[post("/accounts/verify-email-token", data = "")] -async fn post_verify_email_token(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { +async fn post_verify_email_token(data: JsonUpcase, conn: DbConn) -> EmptyResult { let data: VerifyEmailTokenData = data.into_inner().data; - let mut user = match User::find_by_uuid(&data.UserId, &mut conn).await { + let mut user = match User::find_by_uuid(&data.UserId, &conn).await { Some(user) => user, None => err!("User doesn't exist"), }; @@ -676,7 +665,7 @@ async fn post_verify_email_token(data: JsonUpcase, mut con user.verified_at = Some(Utc::now().naive_utc()); user.last_verifying_at = None; user.login_verify_count = 0; - if let Err(e) = user.save(&mut conn).await { + if let Err(e) = user.save(&conn).await { error!("Error saving email verification: {:#?}", e); } @@ -690,11 +679,11 @@ struct DeleteRecoverData { } #[post("/accounts/delete-recover", data = "")] -async fn post_delete_recover(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { +async fn post_delete_recover(data: JsonUpcase, conn: DbConn) -> EmptyResult { let data: DeleteRecoverData = data.into_inner().data; if CONFIG.mail_enabled() { - if let Some(user) = User::find_by_mail(&data.Email, &mut conn).await { + if let Some(user) = User::find_by_mail(&data.Email, &conn).await { if let Err(e) = mail::send_delete_account(&user.email, &user.uuid).await { error!("Error sending delete account email: {:#?}", e); } @@ -717,10 +706,10 @@ struct DeleteRecoverTokenData { } #[post("/accounts/delete-recover-token", data = "")] -async fn post_delete_recover_token(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { +async fn post_delete_recover_token(data: JsonUpcase, conn: DbConn) -> EmptyResult { let data: DeleteRecoverTokenData = data.into_inner().data; - let user = match User::find_by_uuid(&data.UserId, &mut conn).await { + let user = match User::find_by_uuid(&data.UserId, &conn).await { Some(user) => user, None => err!("User doesn't exist"), }; @@ -732,7 +721,7 @@ async fn post_delete_recover_token(data: JsonUpcase, mut if claims.sub != user.uuid { err!("Invalid claim"); } - user.delete(&mut conn).await + user.delete(&conn).await } #[post("/accounts/delete", data = "")] @@ -741,7 +730,7 @@ async fn post_delete_account(data: JsonUpcase, headers: Headers, c } #[delete("/accounts", data = "")] -async fn delete_account(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn delete_account(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { let data: PasswordData = data.into_inner().data; let user = headers.user; @@ -749,7 +738,7 @@ async fn delete_account(data: JsonUpcase, headers: Headers, mut co err!("Invalid password") } - user.delete(&mut conn).await + user.delete(&conn).await } #[get("/accounts/revision-date")] @@ -765,7 +754,7 @@ struct PasswordHintData { } #[post("/accounts/password-hint", data = "")] -async fn password_hint(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { +async fn password_hint(data: JsonUpcase, conn: DbConn) -> EmptyResult { if !CONFIG.mail_enabled() && !CONFIG.show_password_hint() { err!("This server is not configured to provide password hints."); } @@ -775,7 +764,7 @@ async fn password_hint(data: JsonUpcase, mut conn: DbConn) -> let data: PasswordHintData = data.into_inner().data; let email = &data.Email; - match User::find_by_mail(email, &mut conn).await { + match User::find_by_mail(email, &conn).await { None => { // To prevent user enumeration, act as if the user exists. if CONFIG.mail_enabled() { @@ -817,10 +806,10 @@ async fn prelogin(data: JsonUpcase, conn: DbConn) -> Json { _prelogin(data, conn).await } -pub async fn _prelogin(data: JsonUpcase, mut conn: DbConn) -> Json { +pub async fn _prelogin(data: JsonUpcase, conn: DbConn) -> Json { let data: PreloginData = data.into_inner().data; - let (kdf_type, kdf_iter, kdf_mem, kdf_para) = match User::find_by_mail(&data.Email, &mut conn).await { + let (kdf_type, kdf_iter, kdf_mem, kdf_para) = match User::find_by_mail(&data.Email, &conn).await { Some(user) => (user.client_kdf_type, user.client_kdf_iter, user.client_kdf_memory, user.client_kdf_parallelism), None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT, None, None), }; @@ -858,7 +847,7 @@ async fn _api_key( data: JsonUpcase, rotate: bool, headers: Headers, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { use crate::util::format_date; @@ -871,7 +860,7 @@ async fn _api_key( if rotate || user.api_key.is_none() { user.api_key = Some(crypto::generate_api_key()); - user.save(&mut conn).await.expect("Error saving API key"); + user.save(&conn).await.expect("Error saving API key"); } Ok(Json(json!({ @@ -893,11 +882,11 @@ async fn rotate_api_key(data: JsonUpcase, headers: He // This variant is deprecated: https://github.com/bitwarden/server/pull/2682 #[get("/devices/knowndevice//")] -async fn get_known_device_from_path(email: &str, uuid: &str, mut conn: DbConn) -> JsonResult { +async fn get_known_device_from_path(email: &str, uuid: &str, conn: DbConn) -> JsonResult { // This endpoint doesn't have auth header let mut result = false; - if let Some(user) = User::find_by_mail(email, &mut conn).await { - result = Device::find_by_uuid_and_user(uuid, &user.uuid, &mut conn).await.is_some(); + if let Some(user) = User::find_by_mail(email, &conn).await { + result = Device::find_by_uuid_and_user(uuid, &user.uuid, &conn).await.is_some(); } Ok(Json(json!(result))) } @@ -962,14 +951,14 @@ async fn post_device_token(uuid: &str, data: JsonUpcase, headers: Hea } #[put("/devices/identifier//token", data = "")] -async fn put_device_token(uuid: &str, data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn put_device_token(uuid: &str, data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { if !CONFIG.push_enabled() { return Ok(()); } let data = data.into_inner().data; let token = data.PushToken; - let mut device = match Device::find_by_uuid_and_user(&headers.device.uuid, &headers.user.uuid, &mut conn).await { + let mut device = match Device::find_by_uuid_and_user(&headers.device.uuid, &headers.user.uuid, &conn).await { Some(device) => device, None => err!(format!("Error: device {uuid} should be present before a token can be assigned")), }; @@ -977,7 +966,7 @@ async fn put_device_token(uuid: &str, data: JsonUpcase, headers: Head if device.push_uuid.is_none() { device.push_uuid = Some(uuid::Uuid::new_v4().to_string()); } - if let Err(e) = device.save(&mut conn).await { + if let Err(e) = device.save(&conn).await { err!(format!("An error occurred while trying to save the device push token: {e}")); } if let Err(e) = register_push_device(headers.user.uuid, device).await { @@ -988,7 +977,7 @@ async fn put_device_token(uuid: &str, data: JsonUpcase, headers: Head } #[put("/devices/identifier//clear-token")] -async fn put_clear_device_token(uuid: &str, mut conn: DbConn) -> EmptyResult { +async fn put_clear_device_token(uuid: &str, conn: DbConn) -> EmptyResult { // This only clears push token // https://github.com/bitwarden/core/blob/master/src/Api/Controllers/DevicesController.cs#L109 // https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37 @@ -997,8 +986,8 @@ async fn put_clear_device_token(uuid: &str, mut conn: DbConn) -> EmptyResult { return Ok(()); } - if let Some(device) = Device::find_by_uuid(uuid, &mut conn).await { - Device::clear_push_token_by_uuid(uuid, &mut conn).await?; + if let Some(device) = Device::find_by_uuid(uuid, &conn).await { + Device::clear_push_token_by_uuid(uuid, &conn).await?; unregister_push_device(device.uuid).await?; } @@ -1026,12 +1015,12 @@ struct AuthRequestRequest { async fn post_auth_request( data: Json, headers: ClientHeaders, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data = data.into_inner(); - let user = match User::find_by_mail(&data.email, &mut conn).await { + let user = match User::find_by_mail(&data.email, &conn).await { Some(user) => user, None => { err!("AuthRequest doesn't exist") @@ -1046,9 +1035,9 @@ async fn post_auth_request( data.accessCode, data.publicKey, ); - auth_request.save(&mut conn).await?; + auth_request.save(&conn).await?; - nt.send_auth_request(&user.uuid, &auth_request.uuid, &data.deviceIdentifier, &mut conn).await; + nt.send_auth_request(&user.uuid, &auth_request.uuid, &data.deviceIdentifier, &conn).await; Ok(Json(json!({ "id": auth_request.uuid, @@ -1066,8 +1055,8 @@ async fn post_auth_request( } #[get("/auth-requests/")] -async fn get_auth_request(uuid: &str, mut conn: DbConn) -> JsonResult { - let auth_request = match AuthRequest::find_by_uuid(uuid, &mut conn).await { +async fn get_auth_request(uuid: &str, conn: DbConn) -> JsonResult { + let auth_request = match AuthRequest::find_by_uuid(uuid, &conn).await { Some(auth_request) => auth_request, None => { err!("AuthRequest doesn't exist") @@ -1106,12 +1095,12 @@ struct AuthResponseRequest { async fn put_auth_request( uuid: &str, data: Json, - mut conn: DbConn, + conn: DbConn, ant: AnonymousNotify<'_>, nt: Notify<'_>, ) -> JsonResult { let data = data.into_inner(); - let mut auth_request: AuthRequest = match AuthRequest::find_by_uuid(uuid, &mut conn).await { + let mut auth_request: AuthRequest = match AuthRequest::find_by_uuid(uuid, &conn).await { Some(auth_request) => auth_request, None => { err!("AuthRequest doesn't exist") @@ -1122,11 +1111,11 @@ async fn put_auth_request( auth_request.enc_key = Some(data.key); auth_request.master_password_hash = data.masterPasswordHash; auth_request.response_device_id = Some(data.deviceIdentifier.clone()); - auth_request.save(&mut conn).await?; + auth_request.save(&conn).await?; if auth_request.approved.unwrap_or(false) { ant.send_auth_response(&auth_request.user_uuid, &auth_request.uuid).await; - nt.send_auth_response(&auth_request.user_uuid, &auth_request.uuid, data.deviceIdentifier, &mut conn).await; + nt.send_auth_response(&auth_request.user_uuid, &auth_request.uuid, data.deviceIdentifier, &conn).await; } let response_date_utc = auth_request.response_date.map(|response_date| response_date.and_utc()); @@ -1149,8 +1138,8 @@ async fn put_auth_request( } #[get("/auth-requests//response?")] -async fn get_auth_request_response(uuid: &str, code: &str, mut conn: DbConn) -> JsonResult { - let auth_request = match AuthRequest::find_by_uuid(uuid, &mut conn).await { +async fn get_auth_request_response(uuid: &str, code: &str, conn: DbConn) -> JsonResult { + let auth_request = match AuthRequest::find_by_uuid(uuid, &conn).await { Some(auth_request) => auth_request, None => { err!("AuthRequest doesn't exist") @@ -1181,8 +1170,8 @@ async fn get_auth_request_response(uuid: &str, code: &str, mut conn: DbConn) -> } #[get("/auth-requests")] -async fn get_auth_requests(headers: Headers, mut conn: DbConn) -> JsonResult { - let auth_requests = AuthRequest::find_by_user(&headers.user.uuid, &mut conn).await; +async fn get_auth_requests(headers: Headers, conn: DbConn) -> JsonResult { + let auth_requests = AuthRequest::find_by_user(&headers.user.uuid, &conn).await; Ok(Json(json!({ "data": auth_requests @@ -1212,8 +1201,8 @@ async fn get_auth_requests(headers: Headers, mut conn: DbConn) -> JsonResult { pub async fn purge_auth_requests(pool: DbPool) { debug!("Purging auth requests"); - if let Ok(mut conn) = pool.get().await { - AuthRequest::purge_expired_auth_requests(&mut conn).await; + if let Ok(conn) = pool.get().await { + AuthRequest::purge_expired_auth_requests(&conn).await; } else { error!("Failed to get DB connection while purging trashed ciphers") } diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 43e007ab..cce5b077 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -86,8 +86,8 @@ pub fn routes() -> Vec { pub async fn purge_trashed_ciphers(pool: DbPool) { debug!("Purging trashed ciphers"); - if let Ok(mut conn) = pool.get().await { - Cipher::purge_trash(&mut conn).await; + if let Ok(conn) = pool.get().await { + Cipher::purge_trash(&conn).await; } else { error!("Failed to get DB connection while purging trashed ciphers") } @@ -100,37 +100,36 @@ struct SyncData { } #[get("/sync?")] -async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json { - let user_json = headers.user.to_json(&mut conn).await; +async fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json { + let user_json = headers.user.to_json(&conn).await; // Get all ciphers which are visible by the user - let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; + let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &mut conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &conn).await; // Lets generate the ciphers_json using all the gathered info let mut ciphers_json = Vec::with_capacity(ciphers.len()); for c in ciphers { ciphers_json.push( - c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), CipherSyncType::User, &mut conn) - .await, + c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), CipherSyncType::User, &conn).await, ); } - let collections = Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await; + let collections = Collection::find_by_user_uuid(headers.user.uuid.clone(), &conn).await; let mut collections_json = Vec::with_capacity(collections.len()); for c in collections { - collections_json.push(c.to_json_details(&headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); + collections_json.push(c.to_json_details(&headers.user.uuid, Some(&cipher_sync_data), &conn).await); } let folders_json: Vec = - Folder::find_by_user(&headers.user.uuid, &mut conn).await.iter().map(Folder::to_json).collect(); + Folder::find_by_user(&headers.user.uuid, &conn).await.iter().map(Folder::to_json).collect(); let sends_json: Vec = - Send::find_by_user(&headers.user.uuid, &mut conn).await.iter().map(Send::to_json).collect(); + Send::find_by_user(&headers.user.uuid, &conn).await.iter().map(Send::to_json).collect(); let policies_json: Vec = - OrgPolicy::find_confirmed_by_user(&headers.user.uuid, &mut conn).await.iter().map(OrgPolicy::to_json).collect(); + OrgPolicy::find_confirmed_by_user(&headers.user.uuid, &conn).await.iter().map(OrgPolicy::to_json).collect(); let domains_json = if data.exclude_domains { Value::Null @@ -152,15 +151,14 @@ async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json } #[get("/ciphers")] -async fn get_ciphers(headers: Headers, mut conn: DbConn) -> Json { - let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &mut conn).await; +async fn get_ciphers(headers: Headers, conn: DbConn) -> Json { + let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &conn).await; let mut ciphers_json = Vec::with_capacity(ciphers.len()); for c in ciphers { ciphers_json.push( - c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), CipherSyncType::User, &mut conn) - .await, + c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), CipherSyncType::User, &conn).await, ); } @@ -172,17 +170,17 @@ async fn get_ciphers(headers: Headers, mut conn: DbConn) -> Json { } #[get("/ciphers/")] -async fn get_cipher(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { - let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { +async fn get_cipher(uuid: &str, headers: Headers, conn: DbConn) -> JsonResult { + let cipher = match Cipher::find_by_uuid(uuid, &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_accessible_to_user(&headers.user.uuid, &mut conn).await { + if !cipher.is_accessible_to_user(&headers.user.uuid, &conn).await { err!("Cipher is not owned by user") } - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await)) } #[get("/ciphers//admin")] @@ -277,7 +275,7 @@ async fn post_ciphers_admin( async fn post_ciphers_create( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let mut data: ShareCipherData = data.into_inner().data; @@ -291,11 +289,11 @@ async fn post_ciphers_create( // This check is usually only needed in update_cipher_from_data(), but we // need it here as well to avoid creating an empty cipher in the call to // cipher.save() below. - enforce_personal_ownership_policy(Some(&data.Cipher), &headers, &mut conn).await?; + enforce_personal_ownership_policy(Some(&data.Cipher), &headers, &conn).await?; let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone()); cipher.user_uuid = Some(headers.user.uuid.clone()); - cipher.save(&mut conn).await?; + cipher.save(&conn).await?; // When cloning a cipher, the Bitwarden clients seem to set this field // based on the cipher being cloned (when creating a new cipher, it's set @@ -305,12 +303,12 @@ async fn post_ciphers_create( // or otherwise), we can just ignore this field entirely. data.Cipher.LastKnownRevisionDate = None; - share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &nt).await + share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &nt).await } /// Called when creating a new user-owned cipher. #[post("/ciphers", data = "")] -async fn post_ciphers(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { +async fn post_ciphers(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { let mut data: CipherData = data.into_inner().data; // The web/browser clients set this field to null as expected, but the @@ -320,9 +318,9 @@ async fn post_ciphers(data: JsonUpcase, headers: Headers, mut conn: data.LastKnownRevisionDate = None; let mut cipher = Cipher::new(data.Type, data.Name.clone()); - update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::SyncCipherCreate).await?; + update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::SyncCipherCreate).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await)) } /// Enforces the personal ownership policy on user-owned ciphers, if applicable. @@ -332,11 +330,7 @@ async fn post_ciphers(data: JsonUpcase, headers: Headers, mut conn: /// allowed to delete or share such ciphers to an org, however. /// /// Ref: https://bitwarden.com/help/article/policies/#personal-ownership -async fn enforce_personal_ownership_policy( - data: Option<&CipherData>, - headers: &Headers, - conn: &mut DbConn, -) -> EmptyResult { +async fn enforce_personal_ownership_policy(data: Option<&CipherData>, headers: &Headers, conn: &DbConn) -> EmptyResult { if data.is_none() || data.unwrap().OrganizationId.is_none() { let user_uuid = &headers.user.uuid; let policy_type = OrgPolicyType::PersonalOwnership; @@ -352,7 +346,7 @@ pub async fn update_cipher_from_data( data: CipherData, headers: &Headers, shared_to_collection: bool, - conn: &mut DbConn, + conn: &DbConn, nt: &Notify<'_>, ut: UpdateType, ) -> EmptyResult { @@ -541,10 +535,10 @@ struct RelationsData { async fn post_ciphers_import( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - enforce_personal_ownership_policy(None, &headers, &mut conn).await?; + enforce_personal_ownership_policy(None, &headers, &conn).await?; let data: ImportData = data.into_inner().data; @@ -558,7 +552,7 @@ async fn post_ciphers_import( let mut folders: Vec<_> = Vec::new(); for folder in data.Folders.into_iter() { let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.Name); - new_folder.save(&mut conn).await?; + new_folder.save(&conn).await?; folders.push(new_folder); } @@ -576,11 +570,11 @@ async fn post_ciphers_import( cipher_data.FolderId = folder_uuid; let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); - update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None).await?; + update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None).await?; } let mut user = headers.user; - user.update_revision(&mut conn).await?; + user.update_revision(&conn).await?; nt.send_user_update(UpdateType::SyncVault, &user).await; Ok(()) @@ -625,12 +619,12 @@ async fn put_cipher( uuid: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: CipherData = data.into_inner().data; - let mut cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { + let mut cipher = match Cipher::find_by_uuid(uuid, &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; @@ -640,13 +634,13 @@ async fn put_cipher( // cipher itself, so the user shouldn't need write access to change these. // Interestingly, upstream Bitwarden doesn't properly handle this either. - if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { + if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await { err!("Cipher is not write accessible") } - update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::SyncCipherUpdate).await?; + update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::SyncCipherUpdate).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await)) } #[post("/ciphers//partial", data = "")] @@ -665,17 +659,17 @@ async fn put_cipher_partial( uuid: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { let data: PartialCipherData = data.into_inner().data; - let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { + let cipher = match Cipher::find_by_uuid(uuid, &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; if let Some(ref folder_id) = data.FolderId { - match Folder::find_by_uuid(folder_id, &mut conn).await { + match Folder::find_by_uuid(folder_id, &conn).await { Some(folder) => { if folder.user_uuid != headers.user.uuid { err!("Folder is not owned by user") @@ -686,11 +680,11 @@ async fn put_cipher_partial( } // Move cipher - cipher.move_to_folder(data.FolderId.clone(), &headers.user.uuid, &mut conn).await?; + cipher.move_to_folder(data.FolderId.clone(), &headers.user.uuid, &conn).await?; // Update favorite - cipher.set_favorite(Some(data.Favorite), &headers.user.uuid, &mut conn).await?; + cipher.set_favorite(Some(data.Favorite), &headers.user.uuid, &conn).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await)) } #[derive(Deserialize)] @@ -737,35 +731,35 @@ async fn post_collections_admin( uuid: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let data: CollectionsAdminData = data.into_inner().data; - let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { + let cipher = match Cipher::find_by_uuid(uuid, &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { + if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await { err!("Cipher is not write accessible") } let posted_collections: HashSet = data.CollectionIds.iter().cloned().collect(); let current_collections: HashSet = - cipher.get_collections(headers.user.uuid.clone(), &mut conn).await.iter().cloned().collect(); + cipher.get_collections(headers.user.uuid.clone(), &conn).await.iter().cloned().collect(); for collection in posted_collections.symmetric_difference(¤t_collections) { - match Collection::find_by_uuid(collection, &mut conn).await { + match Collection::find_by_uuid(collection, &conn).await { None => err!("Invalid collection ID provided"), Some(collection) => { - if collection.is_writable_by_user(&headers.user.uuid, &mut conn).await { + if collection.is_writable_by_user(&headers.user.uuid, &conn).await { if posted_collections.contains(&collection.uuid) { // Add to collection - CollectionCipher::save(&cipher.uuid, &collection.uuid, &mut conn).await?; + CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn).await?; } else { // Remove from collection - CollectionCipher::delete(&cipher.uuid, &collection.uuid, &mut conn).await?; + CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn).await?; } } else { err!("No rights to modify the collection") @@ -777,10 +771,10 @@ async fn post_collections_admin( nt.send_cipher_update( UpdateType::SyncCipherUpdate, &cipher, - &cipher.update_users_revision(&mut conn).await, + &cipher.update_users_revision(&conn).await, &headers.device.uuid, Some(Vec::from_iter(posted_collections)), - &mut conn, + &conn, ) .await; @@ -791,7 +785,7 @@ async fn post_collections_admin( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; @@ -810,12 +804,12 @@ async fn post_cipher_share( uuid: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: ShareCipherData = data.into_inner().data; - share_cipher_by_uuid(uuid, data, &headers, &mut conn, &nt).await + share_cipher_by_uuid(uuid, data, &headers, &conn, &nt).await } #[put("/ciphers//share", data = "")] @@ -823,12 +817,12 @@ async fn put_cipher_share( uuid: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: ShareCipherData = data.into_inner().data; - share_cipher_by_uuid(uuid, data, &headers, &mut conn, &nt).await + share_cipher_by_uuid(uuid, data, &headers, &conn, &nt).await } #[derive(Deserialize)] @@ -842,7 +836,7 @@ struct ShareSelectedCipherData { async fn put_cipher_share_selected( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let mut data: ShareSelectedCipherData = data.into_inner().data; @@ -870,7 +864,7 @@ async fn put_cipher_share_selected( }; match shared_cipher_data.Cipher.Id.take() { - Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &nt).await?, + Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &conn, &nt).await?, None => err!("Request missing ids field"), }; } @@ -882,7 +876,7 @@ async fn share_cipher_by_uuid( uuid: &str, data: ShareCipherData, headers: &Headers, - conn: &mut DbConn, + conn: &DbConn, nt: &Notify<'_>, ) -> JsonResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { @@ -933,17 +927,17 @@ async fn share_cipher_by_uuid( /// their object storage service. For self-hosted instances, it basically just /// redirects to the same location as before the v2 API. #[get("/ciphers//attachment/")] -async fn get_attachment(uuid: &str, attachment_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { - let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { +async fn get_attachment(uuid: &str, attachment_id: &str, headers: Headers, conn: DbConn) -> JsonResult { + let cipher = match Cipher::find_by_uuid(uuid, &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_accessible_to_user(&headers.user.uuid, &mut conn).await { + if !cipher.is_accessible_to_user(&headers.user.uuid, &conn).await { err!("Cipher is not accessible") } - match Attachment::find_by_id(attachment_id, &mut conn).await { + match Attachment::find_by_id(attachment_id, &conn).await { Some(attachment) if uuid == attachment.cipher_uuid => Ok(Json(attachment.to_json(&headers.host))), Some(_) => err!("Attachment doesn't belong to cipher"), None => err!("Attachment doesn't exist"), @@ -973,14 +967,14 @@ async fn post_attachment_v2( uuid: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { - let cipher = match Cipher::find_by_uuid(uuid, &mut conn).await { + let cipher = match Cipher::find_by_uuid(uuid, &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { + if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await { err!("Cipher is not write accessible") } @@ -988,7 +982,7 @@ async fn post_attachment_v2( let data: AttachmentRequestData = data.into_inner().data; let attachment = Attachment::new(attachment_id.clone(), cipher.uuid.clone(), data.FileName, data.FileSize, Some(data.Key)); - attachment.save(&mut conn).await.expect("Error saving attachment"); + attachment.save(&conn).await.expect("Error saving attachment"); let url = format!("/ciphers/{}/attachment/{}", cipher.uuid, attachment_id); let response_key = match data.AdminRequest { @@ -1001,7 +995,7 @@ async fn post_attachment_v2( "AttachmentId": attachment_id, "Url": url, "FileUploadType": FileUploadType::Direct as i32, - response_key: cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await, + response_key: cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await, }))) } @@ -1024,15 +1018,15 @@ async fn save_attachment( cipher_uuid: &str, data: Form>, headers: &Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> Result<(Cipher, DbConn), crate::error::Error> { - let cipher = match Cipher::find_by_uuid(cipher_uuid, &mut conn).await { + let cipher = match Cipher::find_by_uuid(cipher_uuid, &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { + if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await { err!("Cipher is not write accessible") } @@ -1047,7 +1041,7 @@ async fn save_attachment( match CONFIG.user_attachment_limit() { Some(0) => err!("Attachments are disabled"), Some(limit_kb) => { - let left = (limit_kb * 1024) - Attachment::size_by_user(user_uuid, &mut conn).await + size_adjust; + let left = (limit_kb * 1024) - Attachment::size_by_user(user_uuid, &conn).await + size_adjust; if left <= 0 { err!("Attachment storage limit reached! Delete some attachments to free up space") } @@ -1059,7 +1053,7 @@ async fn save_attachment( match CONFIG.org_attachment_limit() { Some(0) => err!("Attachments are disabled"), Some(limit_kb) => { - let left = (limit_kb * 1024) - Attachment::size_by_org(org_uuid, &mut conn).await + size_adjust; + let left = (limit_kb * 1024) - Attachment::size_by_org(org_uuid, &conn).await + size_adjust; if left <= 0 { err!("Attachment storage limit reached! Delete some attachments to free up space") } @@ -1103,10 +1097,10 @@ async fn save_attachment( if size != attachment.file_size { // Update the attachment with the actual file size. attachment.file_size = size; - attachment.save(&mut conn).await.expect("Error updating attachment"); + attachment.save(&conn).await.expect("Error updating attachment"); } } else { - attachment.delete(&mut conn).await.ok(); + attachment.delete(&conn).await.ok(); err!(format!("Attachment size mismatch (expected within [{min_size}, {max_size}], got {size})")); } @@ -1122,7 +1116,7 @@ async fn save_attachment( } let attachment = Attachment::new(file_id, String::from(cipher_uuid), encrypted_filename.unwrap(), size, data.key); - attachment.save(&mut conn).await.expect("Error saving attachment"); + attachment.save(&conn).await.expect("Error saving attachment"); } if let Err(_err) = data.data.persist_to(&file_path).await { @@ -1132,10 +1126,10 @@ async fn save_attachment( nt.send_cipher_update( UpdateType::SyncCipherUpdate, &cipher, - &cipher.update_users_revision(&mut conn).await, + &cipher.update_users_revision(&conn).await, &headers.device.uuid, None, - &mut conn, + &conn, ) .await; @@ -1147,7 +1141,7 @@ async fn save_attachment( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; } @@ -1165,10 +1159,10 @@ async fn post_attachment_v2_data( attachment_id: &str, data: Form>, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - let attachment = match Attachment::find_by_id(attachment_id, &mut conn).await { + let attachment = match Attachment::find_by_id(attachment_id, &conn).await { Some(attachment) if uuid == attachment.cipher_uuid => Some(attachment), Some(_) => err!("Attachment doesn't belong to cipher"), None => err!("Attachment doesn't exist"), @@ -1192,9 +1186,9 @@ async fn post_attachment( // the attachment database record as well as saving the data to disk. let attachment = None; - let (cipher, mut conn) = save_attachment(attachment, uuid, data, &headers, conn, nt).await?; + let (cipher, conn) = save_attachment(attachment, uuid, data, &headers, conn, nt).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await)) } #[post("/ciphers//attachment-admin", format = "multipart/form-data", data = "")] @@ -1214,10 +1208,10 @@ async fn post_attachment_share( attachment_id: &str, data: Form>, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { - _delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &mut conn, &nt).await?; + _delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &conn, &nt).await?; post_attachment(uuid, data, headers, conn, nt).await } @@ -1248,10 +1242,10 @@ async fn delete_attachment( uuid: &str, attachment_id: &str, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - _delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &mut conn, &nt).await + _delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &conn, &nt).await } #[delete("/ciphers//attachment//admin")] @@ -1259,44 +1253,44 @@ async fn delete_attachment_admin( uuid: &str, attachment_id: &str, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - _delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &mut conn, &nt).await + _delete_cipher_attachment_by_id(uuid, attachment_id, &headers, &conn, &nt).await } #[post("/ciphers//delete")] -async fn delete_cipher_post(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(uuid, &headers, &mut conn, false, &nt).await +async fn delete_cipher_post(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(uuid, &headers, &conn, false, &nt).await // permanent delete } #[post("/ciphers//delete-admin")] -async fn delete_cipher_post_admin(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(uuid, &headers, &mut conn, false, &nt).await +async fn delete_cipher_post_admin(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(uuid, &headers, &conn, false, &nt).await // permanent delete } #[put("/ciphers//delete")] -async fn delete_cipher_put(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(uuid, &headers, &mut conn, true, &nt).await +async fn delete_cipher_put(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(uuid, &headers, &conn, true, &nt).await // soft delete } #[put("/ciphers//delete-admin")] -async fn delete_cipher_put_admin(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(uuid, &headers, &mut conn, true, &nt).await +async fn delete_cipher_put_admin(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(uuid, &headers, &conn, true, &nt).await } #[delete("/ciphers/")] -async fn delete_cipher(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(uuid, &headers, &mut conn, false, &nt).await +async fn delete_cipher(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(uuid, &headers, &conn, false, &nt).await // permanent delete } #[delete("/ciphers//admin")] -async fn delete_cipher_admin(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(uuid, &headers, &mut conn, false, &nt).await +async fn delete_cipher_admin(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(uuid, &headers, &conn, false, &nt).await // permanent delete } @@ -1361,23 +1355,23 @@ async fn delete_cipher_selected_put_admin( } #[put("/ciphers//restore")] -async fn restore_cipher_put(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { - _restore_cipher_by_uuid(uuid, &headers, &mut conn, &nt).await +async fn restore_cipher_put(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { + _restore_cipher_by_uuid(uuid, &headers, &conn, &nt).await } #[put("/ciphers//restore-admin")] -async fn restore_cipher_put_admin(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { - _restore_cipher_by_uuid(uuid, &headers, &mut conn, &nt).await +async fn restore_cipher_put_admin(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { + _restore_cipher_by_uuid(uuid, &headers, &conn, &nt).await } #[put("/ciphers/restore", data = "")] async fn restore_cipher_selected( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { - _restore_multiple_ciphers(data, &headers, &mut conn, &nt).await + _restore_multiple_ciphers(data, &headers, &conn, &nt).await } #[derive(Deserialize)] @@ -1391,14 +1385,14 @@ struct MoveCipherData { async fn move_cipher_selected( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let data = data.into_inner().data; let user_uuid = headers.user.uuid; if let Some(ref folder_id) = data.FolderId { - match Folder::find_by_uuid(folder_id, &mut conn).await { + match Folder::find_by_uuid(folder_id, &conn).await { Some(folder) => { if folder.user_uuid != user_uuid { err!("Folder is not owned by user") @@ -1409,17 +1403,17 @@ async fn move_cipher_selected( } for uuid in data.Ids { - let cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await { + let cipher = match Cipher::find_by_uuid(&uuid, &conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_accessible_to_user(&user_uuid, &mut conn).await { + if !cipher.is_accessible_to_user(&user_uuid, &conn).await { err!("Cipher is not accessible by user") } // Move cipher - cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &mut conn).await?; + cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &conn).await?; nt.send_cipher_update( UpdateType::SyncCipherUpdate, @@ -1427,7 +1421,7 @@ async fn move_cipher_selected( &[user_uuid.clone()], &headers.device.uuid, None, - &mut conn, + &conn, ) .await; } @@ -1456,7 +1450,7 @@ async fn delete_all( organization: Option, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let data: PasswordData = data.into_inner().data; @@ -1471,11 +1465,11 @@ async fn delete_all( match organization { Some(org_data) => { // Organization ID in query params, purging organization vault - match UserOrganization::find_by_user_and_org(&user.uuid, &org_data.org_id, &mut conn).await { + match UserOrganization::find_by_user_and_org(&user.uuid, &org_data.org_id, &conn).await { None => err!("You don't have permission to purge the organization vault"), Some(user_org) => { if user_org.atype == UserOrgType::Owner { - Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?; + Cipher::delete_all_by_organization(&org_data.org_id, &conn).await?; nt.send_user_update(UpdateType::SyncVault, &user).await; log_event( @@ -1485,7 +1479,7 @@ async fn delete_all( user.uuid, headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; @@ -1499,16 +1493,16 @@ async fn delete_all( None => { // No organization ID in query params, purging user vault // Delete ciphers and their attachments - for cipher in Cipher::find_owned_by_user(&user.uuid, &mut conn).await { - cipher.delete(&mut conn).await?; + for cipher in Cipher::find_owned_by_user(&user.uuid, &conn).await { + cipher.delete(&conn).await?; } // Delete folders - for f in Folder::find_by_user(&user.uuid, &mut conn).await { - f.delete(&mut conn).await?; + for f in Folder::find_by_user(&user.uuid, &conn).await { + f.delete(&conn).await?; } - user.update_revision(&mut conn).await?; + user.update_revision(&conn).await?; nt.send_user_update(UpdateType::SyncVault, &user).await; Ok(()) @@ -1519,7 +1513,7 @@ async fn delete_all( async fn _delete_cipher_by_uuid( uuid: &str, headers: &Headers, - conn: &mut DbConn, + conn: &DbConn, soft_delete: bool, nt: &Notify<'_>, ) -> EmptyResult { @@ -1581,7 +1575,7 @@ async fn _delete_cipher_by_uuid( async fn _delete_multiple_ciphers( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, soft_delete: bool, nt: Notify<'_>, ) -> EmptyResult { @@ -1596,7 +1590,7 @@ async fn _delete_multiple_ciphers( }; for uuid in uuids { - if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &mut conn, soft_delete, &nt).await { + if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &conn, soft_delete, &nt).await { return error; }; } @@ -1604,7 +1598,7 @@ async fn _delete_multiple_ciphers( Ok(()) } -async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult { +async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, nt: &Notify<'_>) -> JsonResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), @@ -1646,7 +1640,7 @@ async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbCon async fn _restore_multiple_ciphers( data: JsonUpcase, headers: &Headers, - conn: &mut DbConn, + conn: &DbConn, nt: &Notify<'_>, ) -> JsonResult { let data: Value = data.into_inner().data; @@ -1678,7 +1672,7 @@ async fn _delete_cipher_attachment_by_id( uuid: &str, attachment_id: &str, headers: &Headers, - conn: &mut DbConn, + conn: &DbConn, nt: &Notify<'_>, ) -> EmptyResult { let attachment = match Attachment::find_by_id(attachment_id, conn).await { @@ -1748,7 +1742,7 @@ pub enum CipherSyncType { } impl CipherSyncData { - pub async fn new(user_uuid: &str, sync_type: CipherSyncType, conn: &mut DbConn) -> Self { + pub async fn new(user_uuid: &str, sync_type: CipherSyncType, conn: &DbConn) -> Self { let cipher_folders: HashMap; let cipher_favorites: HashSet; match sync_type { diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs index f5cc6a6e..e2ddd7fa 100644 --- a/src/api/core/emergency_access.rs +++ b/src/api/core/emergency_access.rs @@ -37,13 +37,13 @@ pub fn routes() -> Vec { // region get #[get("/emergency-access/trusted")] -async fn get_contacts(headers: Headers, mut conn: DbConn) -> JsonResult { +async fn get_contacts(headers: Headers, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - let emergency_access_list = EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &mut conn).await; + let emergency_access_list = EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &conn).await; let mut emergency_access_list_json = Vec::with_capacity(emergency_access_list.len()); for ea in emergency_access_list { - emergency_access_list_json.push(ea.to_json_grantee_details(&mut conn).await); + emergency_access_list_json.push(ea.to_json_grantee_details(&conn).await); } Ok(Json(json!({ @@ -54,13 +54,13 @@ async fn get_contacts(headers: Headers, mut conn: DbConn) -> JsonResult { } #[get("/emergency-access/granted")] -async fn get_grantees(headers: Headers, mut conn: DbConn) -> JsonResult { +async fn get_grantees(headers: Headers, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - let emergency_access_list = EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &mut conn).await; + let emergency_access_list = EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &conn).await; let mut emergency_access_list_json = Vec::with_capacity(emergency_access_list.len()); for ea in emergency_access_list { - emergency_access_list_json.push(ea.to_json_grantor_details(&mut conn).await); + emergency_access_list_json.push(ea.to_json_grantor_details(&conn).await); } Ok(Json(json!({ @@ -71,11 +71,11 @@ async fn get_grantees(headers: Headers, mut conn: DbConn) -> JsonResult { } #[get("/emergency-access/")] -async fn get_emergency_access(emer_id: &str, mut conn: DbConn) -> JsonResult { +async fn get_emergency_access(emer_id: &str, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { - Some(emergency_access) => Ok(Json(emergency_access.to_json_grantee_details(&mut conn).await)), + match EmergencyAccess::find_by_uuid(emer_id, &conn).await { + Some(emergency_access) => Ok(Json(emergency_access.to_json_grantee_details(&conn).await)), None => err!("Emergency access not valid."), } } @@ -98,16 +98,12 @@ async fn put_emergency_access(emer_id: &str, data: JsonUpcase", data = "")] -async fn post_emergency_access( - emer_id: &str, - data: JsonUpcase, - mut conn: DbConn, -) -> JsonResult { +async fn post_emergency_access(emer_id: &str, data: JsonUpcase, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; let data: EmergencyAccessUpdateData = data.into_inner().data; - let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emergency_access) => emergency_access, None => err!("Emergency access not valid."), }; @@ -123,7 +119,7 @@ async fn post_emergency_access( emergency_access.key_encrypted = data.KeyEncrypted; } - emergency_access.save(&mut conn).await?; + emergency_access.save(&conn).await?; Ok(Json(emergency_access.to_json())) } @@ -132,12 +128,12 @@ async fn post_emergency_access( // region delete #[delete("/emergency-access/")] -async fn delete_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn delete_emergency_access(emer_id: &str, headers: Headers, conn: DbConn) -> EmptyResult { check_emergency_access_allowed()?; let grantor_user = headers.user; - let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => { if emer.grantor_uuid != grantor_user.uuid && emer.grantee_uuid != Some(grantor_user.uuid) { err!("Emergency access not valid.") @@ -146,7 +142,7 @@ async fn delete_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo } None => err!("Emergency access not valid."), }; - emergency_access.delete(&mut conn).await?; + emergency_access.delete(&conn).await?; Ok(()) } @@ -168,7 +164,7 @@ struct EmergencyAccessInviteData { } #[post("/emergency-access/invite", data = "")] -async fn send_invite(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn send_invite(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { check_emergency_access_allowed()?; let data: EmergencyAccessInviteData = data.into_inner().data; @@ -189,7 +185,7 @@ async fn send_invite(data: JsonUpcase, headers: Heade err!("You can not set yourself as an emergency contact.") } - let grantee_user = match User::find_by_mail(&email, &mut conn).await { + let grantee_user = match User::find_by_mail(&email, &conn).await { None => { if !CONFIG.invitations_allowed() { err!(format!("Grantee user does not exist: {}", &email)) @@ -201,11 +197,11 @@ async fn send_invite(data: JsonUpcase, headers: Heade if !CONFIG.mail_enabled() { let invitation = Invitation::new(&email); - invitation.save(&mut conn).await?; + invitation.save(&conn).await?; } let mut user = User::new(email.clone()); - user.save(&mut conn).await?; + user.save(&conn).await?; user } Some(user) => user, @@ -215,7 +211,7 @@ async fn send_invite(data: JsonUpcase, headers: Heade &grantor_user.uuid, &grantee_user.uuid, &grantee_user.email, - &mut conn, + &conn, ) .await .is_some() @@ -225,7 +221,7 @@ async fn send_invite(data: JsonUpcase, headers: Heade let mut new_emergency_access = EmergencyAccess::new(grantor_user.uuid, grantee_user.email, emergency_access_status, new_type, wait_time_days); - new_emergency_access.save(&mut conn).await?; + new_emergency_access.save(&conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_invite( @@ -238,8 +234,8 @@ async fn send_invite(data: JsonUpcase, headers: Heade .await?; } else { // Automatically mark user as accepted if no email invites - match User::find_by_mail(&email, &mut conn).await { - Some(user) => match accept_invite_process(&user.uuid, &mut new_emergency_access, &email, &mut conn).await { + match User::find_by_mail(&email, &conn).await { + Some(user) => match accept_invite_process(&user.uuid, &mut new_emergency_access, &email, &conn).await { Ok(v) => v, Err(e) => err!(e.to_string()), }, @@ -251,10 +247,10 @@ async fn send_invite(data: JsonUpcase, headers: Heade } #[post("/emergency-access//reinvite")] -async fn resend_invite(emer_id: &str, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn resend_invite(emer_id: &str, headers: Headers, conn: DbConn) -> EmptyResult { check_emergency_access_allowed()?; - let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -272,7 +268,7 @@ async fn resend_invite(emer_id: &str, headers: Headers, mut conn: DbConn) -> Emp None => err!("Email not valid."), }; - let grantee_user = match User::find_by_mail(&email, &mut conn).await { + let grantee_user = match User::find_by_mail(&email, &conn).await { Some(user) => user, None => err!("Grantee user not found."), }; @@ -289,13 +285,13 @@ async fn resend_invite(emer_id: &str, headers: Headers, mut conn: DbConn) -> Emp ) .await?; } else { - if Invitation::find_by_mail(&email, &mut conn).await.is_none() { + if Invitation::find_by_mail(&email, &conn).await.is_none() { let invitation = Invitation::new(&email); - invitation.save(&mut conn).await?; + invitation.save(&conn).await?; } // Automatically mark user as accepted if no email invites - match accept_invite_process(&grantee_user.uuid, &mut emergency_access, &email, &mut conn).await { + match accept_invite_process(&grantee_user.uuid, &mut emergency_access, &email, &conn).await { Ok(v) => v, Err(e) => err!(e.to_string()), } @@ -311,7 +307,7 @@ struct AcceptData { } #[post("/emergency-access//accept", data = "")] -async fn accept_invite(emer_id: &str, data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn accept_invite(emer_id: &str, data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { check_emergency_access_allowed()?; let data: AcceptData = data.into_inner().data; @@ -324,21 +320,21 @@ async fn accept_invite(emer_id: &str, data: JsonUpcase, headers: Hea err!("Claim email does not match current users email") } - let grantee_user = match User::find_by_mail(&claims.email, &mut conn).await { + let grantee_user = match User::find_by_mail(&claims.email, &conn).await { Some(user) => { - Invitation::take(&claims.email, &mut conn).await; + Invitation::take(&claims.email, &conn).await; user } None => err!("Invited user not found"), }; - let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; // get grantor user to send Accepted email - let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { + let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { Some(user) => user, None => err!("Grantor user not found."), }; @@ -347,7 +343,7 @@ async fn accept_invite(emer_id: &str, data: JsonUpcase, headers: Hea && grantor_user.name == claims.grantor_name && grantor_user.email == claims.grantor_email { - match accept_invite_process(&grantee_user.uuid, &mut emergency_access, &grantee_user.email, &mut conn).await { + match accept_invite_process(&grantee_user.uuid, &mut emergency_access, &grantee_user.email, &conn).await { Ok(v) => v, Err(e) => err!(e.to_string()), } @@ -366,7 +362,7 @@ async fn accept_invite_process( grantee_uuid: &str, emergency_access: &mut EmergencyAccess, grantee_email: &str, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { if emergency_access.email.is_none() || emergency_access.email.as_ref().unwrap() != grantee_email { err!("User email does not match invite."); @@ -393,7 +389,7 @@ async fn confirm_emergency_access( emer_id: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { check_emergency_access_allowed()?; @@ -401,7 +397,7 @@ async fn confirm_emergency_access( let data: ConfirmData = data.into_inner().data; let key = data.Key; - let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -412,13 +408,13 @@ async fn confirm_emergency_access( err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&confirming_user.uuid, &mut conn).await { + let grantor_user = match User::find_by_uuid(&confirming_user.uuid, &conn).await { Some(user) => user, None => err!("Grantor user not found."), }; if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { - let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { + let grantee_user = match User::find_by_uuid(grantee_uuid, &conn).await { Some(user) => user, None => err!("Grantee user not found."), }; @@ -427,7 +423,7 @@ async fn confirm_emergency_access( emergency_access.key_encrypted = Some(key); emergency_access.email = None; - emergency_access.save(&mut conn).await?; + emergency_access.save(&conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_invite_confirmed(&grantee_user.email, &grantor_user.name).await?; @@ -443,11 +439,11 @@ async fn confirm_emergency_access( // region access emergency access #[post("/emergency-access//initiate")] -async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn initiate_emergency_access(emer_id: &str, headers: Headers, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; let initiating_user = headers.user; - let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -458,7 +454,7 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { + let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { Some(user) => user, None => err!("Grantor user not found."), }; @@ -468,7 +464,7 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db emergency_access.updated_at = now; emergency_access.recovery_initiated_at = Some(now); emergency_access.last_notification_at = Some(now); - emergency_access.save(&mut conn).await?; + emergency_access.save(&conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_recovery_initiated( @@ -483,10 +479,10 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db } #[post("/emergency-access//approve")] -async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn approve_emergency_access(emer_id: &str, headers: Headers, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -497,19 +493,19 @@ async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbC err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&headers.user.uuid, &mut conn).await { + let grantor_user = match User::find_by_uuid(&headers.user.uuid, &conn).await { Some(user) => user, None => err!("Grantor user not found."), }; if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { - let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { + let grantee_user = match User::find_by_uuid(grantee_uuid, &conn).await { Some(user) => user, None => err!("Grantee user not found."), }; emergency_access.status = EmergencyAccessStatus::RecoveryApproved as i32; - emergency_access.save(&mut conn).await?; + emergency_access.save(&conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_recovery_approved(&grantee_user.email, &grantor_user.name).await?; @@ -521,10 +517,10 @@ async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbC } #[post("/emergency-access//reject")] -async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn reject_emergency_access(emer_id: &str, headers: Headers, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -536,19 +532,19 @@ async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&headers.user.uuid, &mut conn).await { + let grantor_user = match User::find_by_uuid(&headers.user.uuid, &conn).await { Some(user) => user, None => err!("Grantor user not found."), }; if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() { - let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { + let grantee_user = match User::find_by_uuid(grantee_uuid, &conn).await { Some(user) => user, None => err!("Grantee user not found."), }; emergency_access.status = EmergencyAccessStatus::Confirmed as i32; - emergency_access.save(&mut conn).await?; + emergency_access.save(&conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_recovery_rejected(&grantee_user.email, &grantor_user.name).await?; @@ -564,10 +560,10 @@ async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo // region action #[post("/emergency-access//view")] -async fn view_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn view_emergency_access(emer_id: &str, headers: Headers, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -576,8 +572,8 @@ async fn view_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn err!("Emergency access not valid.") } - let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &mut conn).await; - let cipher_sync_data = CipherSyncData::new(&emergency_access.grantor_uuid, CipherSyncType::User, &mut conn).await; + let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &conn).await; + let cipher_sync_data = CipherSyncData::new(&emergency_access.grantor_uuid, CipherSyncType::User, &conn).await; let mut ciphers_json = Vec::with_capacity(ciphers.len()); for c in ciphers { @@ -587,7 +583,7 @@ async fn view_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn &emergency_access.grantor_uuid, Some(&cipher_sync_data), CipherSyncType::User, - &mut conn, + &conn, ) .await, ); @@ -601,11 +597,11 @@ async fn view_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn } #[post("/emergency-access//takeover")] -async fn takeover_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn takeover_emergency_access(emer_id: &str, headers: Headers, conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; let requesting_user = headers.user; - let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -614,7 +610,7 @@ async fn takeover_emergency_access(emer_id: &str, headers: Headers, mut conn: Db err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { + let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { Some(user) => user, None => err!("Grantor user not found."), }; @@ -643,7 +639,7 @@ async fn password_emergency_access( emer_id: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { check_emergency_access_allowed()?; @@ -652,7 +648,7 @@ async fn password_emergency_access( //let key = &data.Key; let requesting_user = headers.user; - let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -661,22 +657,22 @@ async fn password_emergency_access( err!("Emergency access not valid.") } - let mut grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { + let mut grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { Some(user) => user, None => err!("Grantor user not found."), }; // change grantor_user password grantor_user.set_password(new_master_password_hash, Some(data.Key), true, None); - grantor_user.save(&mut conn).await?; + grantor_user.save(&conn).await?; // Disable TwoFactor providers since they will otherwise block logins - TwoFactor::delete_all_by_user(&grantor_user.uuid, &mut conn).await?; + TwoFactor::delete_all_by_user(&grantor_user.uuid, &conn).await?; // Remove grantor from all organisations unless Owner - for user_org in UserOrganization::find_any_state_by_user(&grantor_user.uuid, &mut conn).await { + for user_org in UserOrganization::find_any_state_by_user(&grantor_user.uuid, &conn).await { if user_org.atype != UserOrgType::Owner as i32 { - user_org.delete(&mut conn).await?; + user_org.delete(&conn).await?; } } Ok(()) @@ -685,9 +681,9 @@ async fn password_emergency_access( // endregion #[get("/emergency-access//policies")] -async fn policies_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn policies_emergency_access(emer_id: &str, headers: Headers, conn: DbConn) -> JsonResult { let requesting_user = headers.user; - let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -696,12 +692,12 @@ async fn policies_emergency_access(emer_id: &str, headers: Headers, mut conn: Db err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { + let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { Some(user) => user, None => err!("Grantor user not found."), }; - let policies = OrgPolicy::find_confirmed_by_user(&grantor_user.uuid, &mut conn); + let policies = OrgPolicy::find_confirmed_by_user(&grantor_user.uuid, &conn); let policies_json: Vec = policies.await.iter().map(OrgPolicy::to_json).collect(); Ok(Json(json!({ @@ -735,8 +731,8 @@ pub async fn emergency_request_timeout_job(pool: DbPool) { return; } - if let Ok(mut conn) = pool.get().await { - let emergency_access_list = EmergencyAccess::find_all_recoveries_initiated(&mut conn).await; + if let Ok(conn) = pool.get().await { + let emergency_access_list = EmergencyAccess::find_all_recoveries_initiated(&conn).await; if emergency_access_list.is_empty() { debug!("No emergency request timeout to approve"); @@ -750,18 +746,18 @@ pub async fn emergency_request_timeout_job(pool: DbPool) { if recovery_allowed_at.le(&now) { // Only update the access status // Updating the whole record could cause issues when the emergency_notification_reminder_job is also active - emer.update_access_status_and_save(EmergencyAccessStatus::RecoveryApproved as i32, &now, &mut conn) + emer.update_access_status_and_save(EmergencyAccessStatus::RecoveryApproved as i32, &now, &conn) .await .expect("Unable to update emergency access status"); if CONFIG.mail_enabled() { // get grantor user to send Accepted email let grantor_user = - User::find_by_uuid(&emer.grantor_uuid, &mut conn).await.expect("Grantor user not found"); + User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found"); // get grantee user to send Accepted email let grantee_user = - User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid"), &mut conn) + User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid"), &conn) .await .expect("Grantee user not found"); @@ -790,8 +786,8 @@ pub async fn emergency_notification_reminder_job(pool: DbPool) { return; } - if let Ok(mut conn) = pool.get().await { - let emergency_access_list = EmergencyAccess::find_all_recoveries_initiated(&mut conn).await; + if let Ok(conn) = pool.get().await { + let emergency_access_list = EmergencyAccess::find_all_recoveries_initiated(&conn).await; if emergency_access_list.is_empty() { debug!("No emergency request reminder notification to send"); @@ -812,18 +808,18 @@ pub async fn emergency_notification_reminder_job(pool: DbPool) { if final_recovery_reminder_at.le(&now) && next_recovery_reminder_at.le(&now) { // Only update the last notification date // Updating the whole record could cause issues when the emergency_request_timeout_job is also active - emer.update_last_notification_date_and_save(&now, &mut conn) + emer.update_last_notification_date_and_save(&now, &conn) .await .expect("Unable to update emergency access notification date"); if CONFIG.mail_enabled() { // get grantor user to send Accepted email let grantor_user = - User::find_by_uuid(&emer.grantor_uuid, &mut conn).await.expect("Grantor user not found"); + User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found"); // get grantee user to send Accepted email let grantee_user = - User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid"), &mut conn) + User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid"), &conn) .await .expect("Grantee user not found"); diff --git a/src/api/core/events.rs b/src/api/core/events.rs index 70704d79..c94b38cc 100644 --- a/src/api/core/events.rs +++ b/src/api/core/events.rs @@ -32,7 +32,7 @@ struct EventRange { // Upstream: https://github.com/bitwarden/server/blob/9ecf69d9cabce732cf2c57976dd9afa5728578fb/src/Api/Controllers/EventsController.cs#LL84C35-L84C41 #[get("/organizations//events?")] -async fn get_org_events(org_id: &str, data: EventRange, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_org_events(org_id: &str, data: EventRange, _headers: AdminHeaders, conn: DbConn) -> JsonResult { // Return an empty vec when we org events are disabled. // This prevents client errors let events_json: Vec = if !CONFIG.org_events_enabled() { @@ -45,7 +45,7 @@ async fn get_org_events(org_id: &str, data: EventRange, _headers: AdminHeaders, parse_date(&data.end) }; - Event::find_by_organization_uuid(org_id, &start_date, &end_date, &mut conn) + Event::find_by_organization_uuid(org_id, &start_date, &end_date, &conn) .await .iter() .map(|e| e.to_json()) @@ -60,14 +60,14 @@ async fn get_org_events(org_id: &str, data: EventRange, _headers: AdminHeaders, } #[get("/ciphers//events?")] -async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers, conn: DbConn) -> JsonResult { // Return an empty vec when we org events are disabled. // This prevents client errors let events_json: Vec = if !CONFIG.org_events_enabled() { Vec::with_capacity(0) } else { let mut events_json = Vec::with_capacity(0); - if UserOrganization::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &mut conn).await { + if UserOrganization::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &conn).await { let start_date = parse_date(&data.start); let end_date = if let Some(before_date) = &data.continuation_token { parse_date(before_date) @@ -75,7 +75,7 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers, parse_date(&data.end) }; - events_json = Event::find_by_cipher_uuid(cipher_id, &start_date, &end_date, &mut conn) + events_json = Event::find_by_cipher_uuid(cipher_id, &start_date, &end_date, &conn) .await .iter() .map(|e| e.to_json()) @@ -97,7 +97,7 @@ async fn get_user_events( user_org_id: &str, data: EventRange, _headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { // Return an empty vec when we org events are disabled. // This prevents client errors @@ -111,7 +111,7 @@ async fn get_user_events( parse_date(&data.end) }; - Event::find_by_org_and_user_org(org_id, user_org_id, &start_date, &end_date, &mut conn) + Event::find_by_org_and_user_org(org_id, user_org_id, &start_date, &end_date, &conn) .await .iter() .map(|e| e.to_json()) @@ -161,7 +161,7 @@ struct EventCollection { // https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Events/Controllers/CollectController.cs // https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Services/Implementations/EventService.cs #[post("/collect", format = "application/json", data = "")] -async fn post_events_collect(data: JsonUpcaseVec, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn post_events_collect(data: JsonUpcaseVec, headers: Headers, conn: DbConn) -> EmptyResult { if !CONFIG.org_events_enabled() { return Ok(()); } @@ -176,7 +176,7 @@ async fn post_events_collect(data: JsonUpcaseVec, headers: Head headers.device.atype, Some(event_date), &headers.ip.ip, - &mut conn, + &conn, ) .await; } @@ -190,14 +190,14 @@ async fn post_events_collect(data: JsonUpcaseVec, headers: Head headers.device.atype, Some(event_date), &headers.ip.ip, - &mut conn, + &conn, ) .await; } } _ => { if let Some(cipher_uuid) = &event.CipherId { - if let Some(cipher) = Cipher::find_by_uuid(cipher_uuid, &mut conn).await { + if let Some(cipher) = Cipher::find_by_uuid(cipher_uuid, &conn).await { if let Some(org_uuid) = cipher.organization_uuid { _log_event( event.Type, @@ -207,7 +207,7 @@ async fn post_events_collect(data: JsonUpcaseVec, headers: Head headers.device.atype, Some(event_date), &headers.ip.ip, - &mut conn, + &conn, ) .await; } @@ -219,7 +219,7 @@ async fn post_events_collect(data: JsonUpcaseVec, headers: Head Ok(()) } -pub async fn log_user_event(event_type: i32, user_uuid: &str, device_type: i32, ip: &IpAddr, conn: &mut DbConn) { +pub async fn log_user_event(event_type: i32, user_uuid: &str, device_type: i32, ip: &IpAddr, conn: &DbConn) { if !CONFIG.org_events_enabled() { return; } @@ -232,7 +232,7 @@ async fn _log_user_event( device_type: i32, event_date: Option, ip: &IpAddr, - conn: &mut DbConn, + conn: &DbConn, ) { let orgs = UserOrganization::get_org_uuid_by_user(user_uuid, conn).await; let mut events: Vec = Vec::with_capacity(orgs.len() + 1); // We need an event per org and one without an org @@ -266,7 +266,7 @@ pub async fn log_event( act_user_uuid: String, device_type: i32, ip: &IpAddr, - conn: &mut DbConn, + conn: &DbConn, ) { if !CONFIG.org_events_enabled() { return; @@ -283,7 +283,7 @@ async fn _log_event( device_type: i32, event_date: Option, ip: &IpAddr, - conn: &mut DbConn, + conn: &DbConn, ) { // Create a new empty event let mut event = Event::new(event_type, event_date); @@ -328,8 +328,8 @@ pub async fn event_cleanup_job(pool: DbPool) { return; } - if let Ok(mut conn) = pool.get().await { - Event::clean_events(&mut conn).await.ok(); + if let Ok(conn) = pool.get().await { + Event::clean_events(&conn).await.ok(); } else { error!("Failed to get DB connection while trying to cleanup the events table") } diff --git a/src/api/core/folders.rs b/src/api/core/folders.rs index 3af1285c..d1ba42b3 100644 --- a/src/api/core/folders.rs +++ b/src/api/core/folders.rs @@ -12,8 +12,8 @@ pub fn routes() -> Vec { } #[get("/folders")] -async fn get_folders(headers: Headers, mut conn: DbConn) -> Json { - let folders = Folder::find_by_user(&headers.user.uuid, &mut conn).await; +async fn get_folders(headers: Headers, conn: DbConn) -> Json { + let folders = Folder::find_by_user(&headers.user.uuid, &conn).await; let folders_json: Vec = folders.iter().map(Folder::to_json).collect(); Json(json!({ @@ -24,8 +24,8 @@ async fn get_folders(headers: Headers, mut conn: DbConn) -> Json { } #[get("/folders/")] -async fn get_folder(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { - let folder = match Folder::find_by_uuid(uuid, &mut conn).await { +async fn get_folder(uuid: &str, headers: Headers, conn: DbConn) -> JsonResult { + let folder = match Folder::find_by_uuid(uuid, &conn).await { Some(folder) => folder, _ => err!("Invalid folder"), }; @@ -44,13 +44,13 @@ pub struct FolderData { } #[post("/folders", data = "")] -async fn post_folders(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { +async fn post_folders(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { let data: FolderData = data.into_inner().data; let mut folder = Folder::new(headers.user.uuid, data.Name); - folder.save(&mut conn).await?; - nt.send_folder_update(UpdateType::SyncFolderCreate, &folder, &headers.device.uuid, &mut conn).await; + folder.save(&conn).await?; + nt.send_folder_update(UpdateType::SyncFolderCreate, &folder, &headers.device.uuid, &conn).await; Ok(Json(folder.to_json())) } @@ -71,12 +71,12 @@ async fn put_folder( uuid: &str, data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: FolderData = data.into_inner().data; - let mut folder = match Folder::find_by_uuid(uuid, &mut conn).await { + let mut folder = match Folder::find_by_uuid(uuid, &conn).await { Some(folder) => folder, _ => err!("Invalid folder"), }; @@ -87,8 +87,8 @@ async fn put_folder( folder.name = data.Name; - folder.save(&mut conn).await?; - nt.send_folder_update(UpdateType::SyncFolderUpdate, &folder, &headers.device.uuid, &mut conn).await; + folder.save(&conn).await?; + nt.send_folder_update(UpdateType::SyncFolderUpdate, &folder, &headers.device.uuid, &conn).await; Ok(Json(folder.to_json())) } @@ -99,8 +99,8 @@ async fn delete_folder_post(uuid: &str, headers: Headers, conn: DbConn, nt: Noti } #[delete("/folders/")] -async fn delete_folder(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - let folder = match Folder::find_by_uuid(uuid, &mut conn).await { +async fn delete_folder(uuid: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + let folder = match Folder::find_by_uuid(uuid, &conn).await { Some(folder) => folder, _ => err!("Invalid folder"), }; @@ -110,8 +110,8 @@ async fn delete_folder(uuid: &str, headers: Headers, mut conn: DbConn, nt: Notif } // Delete the actual folder entry - folder.delete(&mut conn).await?; + folder.delete(&conn).await?; - nt.send_folder_update(UpdateType::SyncFolderDelete, &folder, &headers.device.uuid, &mut conn).await; + nt.send_folder_update(UpdateType::SyncFolderDelete, &folder, &headers.device.uuid, &conn).await; Ok(()) } diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index f1424688..7d4da961 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -108,7 +108,7 @@ struct EquivDomainData { async fn post_eq_domains( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: EquivDomainData = data.into_inner().data; @@ -122,7 +122,7 @@ async fn post_eq_domains( user.excluded_globals = to_string(&excluded_globals).unwrap_or_else(|_| "[]".to_string()); user.equivalent_domains = to_string(&equivalent_domains).unwrap_or_else(|_| "[]".to_string()); - user.save(&mut conn).await?; + user.save(&conn).await?; nt.send_user_update(UpdateType::SyncSettings, &user).await; diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 163c42ef..82ab4395 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -149,11 +149,11 @@ struct OrgBulkIds { } #[post("/organizations", data = "")] -async fn create_organization(headers: Headers, data: JsonUpcase, mut conn: DbConn) -> JsonResult { +async fn create_organization(headers: Headers, data: JsonUpcase, conn: DbConn) -> JsonResult { if !CONFIG.is_org_creation_allowed(&headers.user.email) { err!("User not allowed to create organizations") } - if OrgPolicy::is_applicable_to_user(&headers.user.uuid, OrgPolicyType::SingleOrg, None, &mut conn).await { + if OrgPolicy::is_applicable_to_user(&headers.user.uuid, OrgPolicyType::SingleOrg, None, &conn).await { err!( "You may not create an organization. You belong to an organization which has a policy that prohibits you from being a member of any other organization." ) @@ -176,9 +176,9 @@ async fn create_organization(headers: Headers, data: JsonUpcase, mut co user_org.atype = UserOrgType::Owner as i32; user_org.status = UserOrgStatus::Confirmed as i32; - org.save(&mut conn).await?; - user_org.save(&mut conn).await?; - collection.save(&mut conn).await?; + org.save(&conn).await?; + user_org.save(&conn).await?; + collection.save(&conn).await?; Ok(Json(org.to_json())) } @@ -188,7 +188,7 @@ async fn delete_organization( org_id: &str, data: JsonUpcase, headers: OwnerHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { let data: PasswordData = data.into_inner().data; let password_hash = data.MasterPasswordHash; @@ -197,9 +197,9 @@ async fn delete_organization( err!("Invalid password") } - match Organization::find_by_uuid(org_id, &mut conn).await { + match Organization::find_by_uuid(org_id, &conn).await { None => err!("Organization not found"), - Some(org) => org.delete(&mut conn).await, + Some(org) => org.delete(&conn).await, } } @@ -214,12 +214,12 @@ async fn post_delete_organization( } #[post("/organizations//leave")] -async fn leave_organization(org_id: &str, headers: Headers, mut conn: DbConn) -> EmptyResult { - match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { +async fn leave_organization(org_id: &str, headers: Headers, conn: DbConn) -> EmptyResult { + match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &conn).await { None => err!("User not part of organization"), Some(user_org) => { if user_org.atype == UserOrgType::Owner - && UserOrganization::count_confirmed_by_org_and_type(org_id, UserOrgType::Owner, &mut conn).await <= 1 + && UserOrganization::count_confirmed_by_org_and_type(org_id, UserOrgType::Owner, &conn).await <= 1 { err!("The last owner can't leave") } @@ -231,18 +231,18 @@ async fn leave_organization(org_id: &str, headers: Headers, mut conn: DbConn) -> headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - user_org.delete(&mut conn).await + user_org.delete(&conn).await } } } #[get("/organizations/")] -async fn get_organization(org_id: &str, _headers: OwnerHeaders, mut conn: DbConn) -> JsonResult { - match Organization::find_by_uuid(org_id, &mut conn).await { +async fn get_organization(org_id: &str, _headers: OwnerHeaders, conn: DbConn) -> JsonResult { + match Organization::find_by_uuid(org_id, &conn).await { Some(organization) => Ok(Json(organization.to_json())), None => err!("Can't find organization details"), } @@ -263,11 +263,11 @@ async fn post_organization( org_id: &str, headers: OwnerHeaders, data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { let data: OrganizationUpdateData = data.into_inner().data; - let mut org = match Organization::find_by_uuid(org_id, &mut conn).await { + let mut org = match Organization::find_by_uuid(org_id, &conn).await { Some(organization) => organization, None => err!("Can't find organization details"), }; @@ -275,7 +275,7 @@ async fn post_organization( org.name = data.Name; org.billing_email = data.BillingEmail; - org.save(&mut conn).await?; + org.save(&conn).await?; log_event( EventType::OrganizationUpdated as i32, @@ -284,7 +284,7 @@ async fn post_organization( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; @@ -293,10 +293,10 @@ async fn post_organization( // GET /api/collections?writeOnly=false #[get("/collections")] -async fn get_user_collections(headers: Headers, mut conn: DbConn) -> Json { +async fn get_user_collections(headers: Headers, conn: DbConn) -> Json { Json(json!({ "Data": - Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await + Collection::find_by_user_uuid(headers.user.uuid.clone(), &conn).await .iter() .map(Collection::to_json) .collect::(), @@ -306,28 +306,28 @@ async fn get_user_collections(headers: Headers, mut conn: DbConn) -> Json } #[get("/organizations//collections")] -async fn get_org_collections(org_id: &str, _headers: ManagerHeadersLoose, mut conn: DbConn) -> Json { +async fn get_org_collections(org_id: &str, _headers: ManagerHeadersLoose, conn: DbConn) -> Json { Json(json!({ - "Data": _get_org_collections(org_id, &mut conn).await, + "Data": _get_org_collections(org_id, &conn).await, "Object": "list", "ContinuationToken": null, })) } #[get("/organizations//collections/details")] -async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { +async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, conn: DbConn) -> JsonResult { let mut data = Vec::new(); - let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { + let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &conn).await { Some(u) => u, None => err!("User is not part of organization"), }; - let coll_users = CollectionUser::find_by_organization(org_id, &mut conn).await; + let coll_users = CollectionUser::find_by_organization(org_id, &conn).await; - for col in Collection::find_by_organization(org_id, &mut conn).await { + for col in Collection::find_by_organization(org_id, &conn).await { let groups: Vec = if CONFIG.org_groups_enabled() { - CollectionGroup::find_by_collection(&col.uuid, &mut conn) + CollectionGroup::find_by_collection(&col.uuid, &conn) .await .iter() .map(|collection_group| { @@ -373,7 +373,7 @@ async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, }))) } -async fn _get_org_collections(org_id: &str, conn: &mut DbConn) -> Value { +async fn _get_org_collections(org_id: &str, conn: &DbConn) -> Value { Collection::find_by_organization(org_id, conn).await.iter().map(Collection::to_json).collect::() } @@ -382,17 +382,17 @@ async fn post_organization_collections( org_id: &str, headers: ManagerHeadersLoose, data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { let data: NewCollectionData = data.into_inner().data; - let org = match Organization::find_by_uuid(org_id, &mut conn).await { + let org = match Organization::find_by_uuid(org_id, &conn).await { Some(organization) => organization, None => err!("Can't find organization details"), }; let collection = Collection::new(org.uuid, data.Name, data.ExternalId); - collection.save(&mut conn).await?; + collection.save(&conn).await?; log_event( EventType::CollectionCreated as i32, @@ -401,18 +401,18 @@ async fn post_organization_collections( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; for group in data.Groups { CollectionGroup::new(collection.uuid.clone(), group.Id, group.ReadOnly, group.HidePasswords) - .save(&mut conn) + .save(&conn) .await?; } for user in data.Users { - let org_user = match UserOrganization::find_by_uuid(&user.Id, &mut conn).await { + let org_user = match UserOrganization::find_by_uuid(&user.Id, &conn).await { Some(u) => u, None => err!("User is not part of organization"), }; @@ -421,12 +421,11 @@ async fn post_organization_collections( continue; } - CollectionUser::save(&org_user.user_uuid, &collection.uuid, user.ReadOnly, user.HidePasswords, &mut conn) - .await?; + CollectionUser::save(&org_user.user_uuid, &collection.uuid, user.ReadOnly, user.HidePasswords, &conn).await?; } if headers.org_user.atype == UserOrgType::Manager && !headers.org_user.access_all { - CollectionUser::save(&headers.org_user.user_uuid, &collection.uuid, false, false, &mut conn).await?; + CollectionUser::save(&headers.org_user.user_uuid, &collection.uuid, false, false, &conn).await?; } Ok(Json(collection.to_json())) @@ -449,16 +448,16 @@ async fn post_organization_collection_update( col_id: &str, headers: ManagerHeaders, data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { let data: NewCollectionData = data.into_inner().data; - let org = match Organization::find_by_uuid(org_id, &mut conn).await { + let org = match Organization::find_by_uuid(org_id, &conn).await { Some(organization) => organization, None => err!("Can't find organization details"), }; - let mut collection = match Collection::find_by_uuid(col_id, &mut conn).await { + let mut collection = match Collection::find_by_uuid(col_id, &conn).await { Some(collection) => collection, None => err!("Collection not found"), }; @@ -473,7 +472,7 @@ async fn post_organization_collection_update( _ => None, }; - collection.save(&mut conn).await?; + collection.save(&conn).await?; log_event( EventType::CollectionUpdated as i32, @@ -482,22 +481,20 @@ async fn post_organization_collection_update( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - CollectionGroup::delete_all_by_collection(col_id, &mut conn).await?; + CollectionGroup::delete_all_by_collection(col_id, &conn).await?; for group in data.Groups { - CollectionGroup::new(String::from(col_id), group.Id, group.ReadOnly, group.HidePasswords) - .save(&mut conn) - .await?; + CollectionGroup::new(String::from(col_id), group.Id, group.ReadOnly, group.HidePasswords).save(&conn).await?; } - CollectionUser::delete_all_by_collection(col_id, &mut conn).await?; + CollectionUser::delete_all_by_collection(col_id, &conn).await?; for user in data.Users { - let org_user = match UserOrganization::find_by_uuid(&user.Id, &mut conn).await { + let org_user = match UserOrganization::find_by_uuid(&user.Id, &conn).await { Some(u) => u, None => err!("User is not part of organization"), }; @@ -506,7 +503,7 @@ async fn post_organization_collection_update( continue; } - CollectionUser::save(&org_user.user_uuid, col_id, user.ReadOnly, user.HidePasswords, &mut conn).await?; + CollectionUser::save(&org_user.user_uuid, col_id, user.ReadOnly, user.HidePasswords, &conn).await?; } Ok(Json(collection.to_json())) @@ -518,9 +515,9 @@ async fn delete_organization_collection_user( col_id: &str, org_user_id: &str, _headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { - let collection = match Collection::find_by_uuid(col_id, &mut conn).await { + let collection = match Collection::find_by_uuid(col_id, &conn).await { None => err!("Collection not found"), Some(collection) => { if collection.org_uuid == org_id { @@ -531,12 +528,12 @@ async fn delete_organization_collection_user( } }; - match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { + match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &conn).await { None => err!("User not found in organization"), Some(user_org) => { - match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &mut conn).await { + match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &conn).await { None => err!("User not assigned to collection"), - Some(col_user) => col_user.delete(&mut conn).await, + Some(col_user) => col_user.delete(&conn).await, } } } @@ -557,7 +554,7 @@ async fn _delete_organization_collection( org_id: &str, col_id: &str, headers: &ManagerHeaders, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { match Collection::find_by_uuid(col_id, conn).await { None => err!("Collection not found"), @@ -586,9 +583,9 @@ async fn delete_organization_collection( org_id: &str, col_id: &str, headers: ManagerHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { - _delete_organization_collection(org_id, col_id, &headers, &mut conn).await + _delete_organization_collection(org_id, col_id, &headers, &conn).await } #[derive(Deserialize, Debug)] @@ -604,9 +601,9 @@ async fn post_organization_collection_delete( col_id: &str, headers: ManagerHeaders, _data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { - _delete_organization_collection(org_id, col_id, &headers, &mut conn).await + _delete_organization_collection(org_id, col_id, &headers, &conn).await } #[derive(Deserialize, Debug)] @@ -621,7 +618,7 @@ async fn bulk_delete_organization_collections( org_id: &str, headers: ManagerHeadersLoose, data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { let data: BulkCollectionIds = data.into_inner().data; if org_id != data.OrganizationId { @@ -630,35 +627,30 @@ async fn bulk_delete_organization_collections( let collections = data.Ids; - let headers = ManagerHeaders::from_loose(headers, &collections, &mut conn).await?; + let headers = ManagerHeaders::from_loose(headers, &collections, &conn).await?; for col_id in collections { - _delete_organization_collection(org_id, &col_id, &headers, &mut conn).await? + _delete_organization_collection(org_id, &col_id, &headers, &conn).await? } Ok(()) } #[get("/organizations//collections//details")] -async fn get_org_collection_detail( - org_id: &str, - coll_id: &str, - headers: ManagerHeaders, - mut conn: DbConn, -) -> JsonResult { - match Collection::find_by_uuid_and_user(coll_id, headers.user.uuid.clone(), &mut conn).await { +async fn get_org_collection_detail(org_id: &str, coll_id: &str, headers: ManagerHeaders, conn: DbConn) -> JsonResult { + match Collection::find_by_uuid_and_user(coll_id, headers.user.uuid.clone(), &conn).await { None => err!("Collection not found"), Some(collection) => { if collection.org_uuid != org_id { err!("Collection is not owned by organization") } - let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { + let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &conn).await { Some(u) => u, None => err!("User is not part of organization"), }; let groups: Vec = if CONFIG.org_groups_enabled() { - CollectionGroup::find_by_collection(&collection.uuid, &mut conn) + CollectionGroup::find_by_collection(&collection.uuid, &conn) .await .iter() .map(|collection_group| { @@ -673,7 +665,7 @@ async fn get_org_collection_detail( let mut assigned = false; let users: Vec = - CollectionUser::find_by_collection_swap_user_uuid_with_org_user_uuid(&collection.uuid, &mut conn) + CollectionUser::find_by_collection_swap_user_uuid_with_org_user_uuid(&collection.uuid, &conn) .await .iter() .map(|collection_user| { @@ -702,17 +694,17 @@ async fn get_org_collection_detail( } #[get("/organizations//collections//users")] -async fn get_collection_users(org_id: &str, coll_id: &str, _headers: ManagerHeaders, mut conn: DbConn) -> JsonResult { +async fn get_collection_users(org_id: &str, coll_id: &str, _headers: ManagerHeaders, conn: DbConn) -> JsonResult { // Get org and collection, check that collection is from org - let collection = match Collection::find_by_uuid_and_org(coll_id, org_id, &mut conn).await { + let collection = match Collection::find_by_uuid_and_org(coll_id, org_id, &conn).await { None => err!("Collection not found in Organization"), Some(collection) => collection, }; let mut user_list = Vec::new(); - for col_user in CollectionUser::find_by_collection(&collection.uuid, &mut conn).await { + for col_user in CollectionUser::find_by_collection(&collection.uuid, &conn).await { user_list.push( - UserOrganization::find_by_user_and_org(&col_user.user_uuid, org_id, &mut conn) + UserOrganization::find_by_user_and_org(&col_user.user_uuid, org_id, &conn) .await .unwrap() .to_json_user_access_restrictions(&col_user), @@ -728,19 +720,19 @@ async fn put_collection_users( coll_id: &str, data: JsonUpcaseVec, _headers: ManagerHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { // Get org and collection, check that collection is from org - if Collection::find_by_uuid_and_org(coll_id, org_id, &mut conn).await.is_none() { + if Collection::find_by_uuid_and_org(coll_id, org_id, &conn).await.is_none() { err!("Collection not found in Organization") } // Delete all the user-collections - CollectionUser::delete_all_by_collection(coll_id, &mut conn).await?; + CollectionUser::delete_all_by_collection(coll_id, &conn).await?; // And then add all the received ones (except if the user has access_all) for d in data.iter().map(|d| &d.data) { - let user = match UserOrganization::find_by_uuid(&d.Id, &mut conn).await { + let user = match UserOrganization::find_by_uuid(&d.Id, &conn).await { Some(u) => u, None => err!("User is not part of organization"), }; @@ -749,7 +741,7 @@ async fn put_collection_users( continue; } - CollectionUser::save(&user.user_uuid, coll_id, d.ReadOnly, d.HidePasswords, &mut conn).await?; + CollectionUser::save(&user.user_uuid, coll_id, d.ReadOnly, d.HidePasswords, &conn).await?; } Ok(()) @@ -762,15 +754,15 @@ struct OrgIdData { } #[get("/ciphers/organization-details?")] -async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) -> Json { +async fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> Json { Json(json!({ - "Data": _get_org_details(&data.organization_id, &headers.host, &headers.user.uuid, &mut conn).await, + "Data": _get_org_details(&data.organization_id, &headers.host, &headers.user.uuid, &conn).await, "Object": "list", "ContinuationToken": null, })) } -async fn _get_org_details(org_id: &str, host: &str, user_uuid: &str, conn: &mut DbConn) -> Value { +async fn _get_org_details(org_id: &str, host: &str, user_uuid: &str, conn: &DbConn) -> Value { let ciphers = Cipher::find_by_org(org_id, conn).await; let cipher_sync_data = CipherSyncData::new(user_uuid, CipherSyncType::Organization, conn).await; @@ -791,19 +783,14 @@ struct GetOrgUserData { } #[get("/organizations//users?")] -async fn get_org_users( - data: GetOrgUserData, - org_id: &str, - _headers: ManagerHeadersLoose, - mut conn: DbConn, -) -> Json { +async fn get_org_users(data: GetOrgUserData, org_id: &str, _headers: ManagerHeadersLoose, conn: DbConn) -> Json { let mut users_json = Vec::new(); - for u in UserOrganization::find_by_org(org_id, &mut conn).await { + for u in UserOrganization::find_by_org(org_id, &conn).await { users_json.push( u.to_json_user_details( data.include_collections.unwrap_or(false), data.include_groups.unwrap_or(false), - &mut conn, + &conn, ) .await, ); @@ -817,15 +804,10 @@ async fn get_org_users( } #[post("/organizations//keys", data = "")] -async fn post_org_keys( - org_id: &str, - data: JsonUpcase, - _headers: AdminHeaders, - mut conn: DbConn, -) -> JsonResult { +async fn post_org_keys(org_id: &str, data: JsonUpcase, _headers: AdminHeaders, conn: DbConn) -> JsonResult { let data: OrgKeyData = data.into_inner().data; - let mut org = match Organization::find_by_uuid(org_id, &mut conn).await { + let mut org = match Organization::find_by_uuid(org_id, &conn).await { Some(organization) => { if organization.private_key.is_some() && organization.public_key.is_some() { err!("Organization Keys already exist") @@ -838,7 +820,7 @@ async fn post_org_keys( org.private_key = Some(data.EncryptedPrivateKey); org.public_key = Some(data.PublicKey); - org.save(&mut conn).await?; + org.save(&conn).await?; Ok(Json(json!({ "Object": "organizationKeys", @@ -866,12 +848,7 @@ struct InviteData { } #[post("/organizations//users/invite", data = "")] -async fn send_invite( - org_id: &str, - data: JsonUpcase, - headers: AdminHeaders, - mut conn: DbConn, -) -> EmptyResult { +async fn send_invite(org_id: &str, data: JsonUpcase, headers: AdminHeaders, conn: DbConn) -> EmptyResult { let data: InviteData = data.into_inner().data; let new_type = match UserOrgType::from_str(&data.Type.into_string()) { @@ -886,7 +863,7 @@ async fn send_invite( for email in data.Emails.iter() { let email = email.to_lowercase(); let mut user_org_status = UserOrgStatus::Invited as i32; - let user = match User::find_by_mail(&email, &mut conn).await { + let user = match User::find_by_mail(&email, &conn).await { None => { if !CONFIG.invitations_allowed() { err!(format!("User does not exist: {email}")) @@ -898,15 +875,15 @@ async fn send_invite( if !CONFIG.mail_enabled() { let invitation = Invitation::new(&email); - invitation.save(&mut conn).await?; + invitation.save(&conn).await?; } let mut user = User::new(email.clone()); - user.save(&mut conn).await?; + user.save(&conn).await?; user } Some(user) => { - if UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await.is_some() { + if UserOrganization::find_by_user_and_org(&user.uuid, org_id, &conn).await.is_some() { err!(format!("User already in organization: {email}")) } else { // automatically accept existing users if mail is disabled @@ -927,21 +904,21 @@ async fn send_invite( // If no accessAll, add the collections received if !access_all { for col in data.Collections.iter().flatten() { - match Collection::find_by_uuid_and_org(&col.Id, org_id, &mut conn).await { + match Collection::find_by_uuid_and_org(&col.Id, org_id, &conn).await { None => err!("Collection not found in Organization"), Some(collection) => { - CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, col.HidePasswords, &mut conn) + CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, col.HidePasswords, &conn) .await?; } } } } - new_user.save(&mut conn).await?; + new_user.save(&conn).await?; for group in data.Groups.iter() { let mut group_entry = GroupUser::new(String::from(group), user.uuid.clone()); - group_entry.save(&mut conn).await?; + group_entry.save(&conn).await?; } log_event( @@ -951,12 +928,12 @@ async fn send_invite( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; if CONFIG.mail_enabled() { - let org_name = match Organization::find_by_uuid(org_id, &mut conn).await { + let org_name = match Organization::find_by_uuid(org_id, &conn).await { Some(org) => org.name, None => err!("Error looking up organization"), }; @@ -981,13 +958,13 @@ async fn bulk_reinvite_user( org_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> Json { let data: OrgBulkIds = data.into_inner().data; let mut bulk_response = Vec::new(); for org_user_id in data.Ids { - let err_msg = match _reinvite_user(org_id, &org_user_id, &headers.user.email, &mut conn).await { + let err_msg = match _reinvite_user(org_id, &org_user_id, &headers.user.email, &conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1009,11 +986,11 @@ async fn bulk_reinvite_user( } #[post("/organizations//users//reinvite")] -async fn reinvite_user(org_id: &str, user_org: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { - _reinvite_user(org_id, user_org, &headers.user.email, &mut conn).await +async fn reinvite_user(org_id: &str, user_org: &str, headers: AdminHeaders, conn: DbConn) -> EmptyResult { + _reinvite_user(org_id, user_org, &headers.user.email, &conn).await } -async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &mut DbConn) -> EmptyResult { +async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &DbConn) -> EmptyResult { if !CONFIG.invitations_allowed() { err!("Invitations are not allowed.") } @@ -1067,22 +1044,17 @@ struct AcceptData { } #[post("/organizations//users/<_org_user_id>/accept", data = "")] -async fn accept_invite( - org_id: &str, - _org_user_id: &str, - data: JsonUpcase, - mut conn: DbConn, -) -> EmptyResult { +async fn accept_invite(org_id: &str, _org_user_id: &str, data: JsonUpcase, conn: DbConn) -> EmptyResult { // The web-vault passes org_id and org_user_id in the URL, but we are just reading them from the JWT instead let data: AcceptData = data.into_inner().data; let claims = decode_invite(&data.Token)?; - match User::find_by_mail(&claims.email, &mut conn).await { + match User::find_by_mail(&claims.email, &conn).await { Some(_) => { - Invitation::take(&claims.email, &mut conn).await; + Invitation::take(&claims.email, &conn).await; if let (Some(user_org), Some(org)) = (&claims.user_org_id, &claims.org_id) { - let mut user_org = match UserOrganization::find_by_uuid_and_org(user_org, org, &mut conn).await { + let mut user_org = match UserOrganization::find_by_uuid_and_org(user_org, org, &conn).await { Some(user_org) => user_org, None => err!("Error accepting the invitation"), }; @@ -1091,7 +1063,7 @@ async fn accept_invite( err!("User already accepted the invitation") } - let master_password_required = OrgPolicy::org_is_reset_password_auto_enroll(org, &mut conn).await; + let master_password_required = OrgPolicy::org_is_reset_password_auto_enroll(org, &conn).await; if data.ResetPasswordKey.is_none() && master_password_required { err!("Reset password key is required, but not provided."); } @@ -1099,7 +1071,7 @@ async fn accept_invite( // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type // It returns different error messages per function. if user_org.atype < UserOrgType::Admin { - match OrgPolicy::is_user_allowed(&user_org.user_uuid, org_id, false, &mut conn).await { + match OrgPolicy::is_user_allowed(&user_org.user_uuid, org_id, false, &conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { err!("You cannot join this organization until you enable two-step login on your user account"); @@ -1116,7 +1088,7 @@ async fn accept_invite( user_org.reset_password_key = data.ResetPasswordKey; } - user_org.save(&mut conn).await?; + user_org.save(&conn).await?; } } None => err!("Invited user not found"), @@ -1125,7 +1097,7 @@ async fn accept_invite( if CONFIG.mail_enabled() { let mut org_name = CONFIG.invitation_org_name(); if let Some(org_id) = &claims.org_id { - org_name = match Organization::find_by_uuid(org_id, &mut conn).await { + org_name = match Organization::find_by_uuid(org_id, &conn).await { Some(org) => org.name, None => err!("Organization not found."), }; @@ -1147,7 +1119,7 @@ async fn bulk_confirm_invite( org_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> Json { let data = data.into_inner().data; @@ -1158,7 +1130,7 @@ async fn bulk_confirm_invite( for invite in keys { let org_user_id = invite["Id"].as_str().unwrap_or_default(); let user_key = invite["Key"].as_str().unwrap_or_default(); - let err_msg = match _confirm_invite(org_id, org_user_id, user_key, &headers, &mut conn, &nt).await { + let err_msg = match _confirm_invite(org_id, org_user_id, user_key, &headers, &conn, &nt).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1188,12 +1160,12 @@ async fn confirm_invite( org_user_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let data = data.into_inner().data; let user_key = data["Key"].as_str().unwrap_or_default(); - _confirm_invite(org_id, org_user_id, user_key, &headers, &mut conn, &nt).await + _confirm_invite(org_id, org_user_id, user_key, &headers, &conn, &nt).await } async fn _confirm_invite( @@ -1201,7 +1173,7 @@ async fn _confirm_invite( org_user_id: &str, key: &str, headers: &AdminHeaders, - conn: &mut DbConn, + conn: &DbConn, nt: &Notify<'_>, ) -> EmptyResult { if key.is_empty() || org_user_id.is_empty() { @@ -1276,9 +1248,9 @@ async fn get_user( org_user_id: &str, data: GetOrgUserData, _headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { - let user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { + let user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &conn).await { Some(user) => user, None => err!("The specified user isn't a member of the organization"), }; @@ -1286,9 +1258,7 @@ async fn get_user( // In this case, when groups are requested we also need to include collections. // Else these will not be shown in the interface, and could lead to missing collections when saved. let include_groups = data.include_groups.unwrap_or(false); - Ok(Json( - user.to_json_user_details(data.include_collections.unwrap_or(include_groups), include_groups, &mut conn).await, - )) + Ok(Json(user.to_json_user_details(data.include_collections.unwrap_or(include_groups), include_groups, &conn).await)) } #[derive(Deserialize)] @@ -1317,7 +1287,7 @@ async fn edit_user( org_user_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { let data: EditUserData = data.into_inner().data; @@ -1326,7 +1296,7 @@ async fn edit_user( None => err!("Invalid type"), }; - let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { + let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &conn).await { Some(user) => user, None => err!("The specified user isn't member of the organization"), }; @@ -1347,7 +1317,7 @@ async fn edit_user( && user_to_edit.status == UserOrgStatus::Confirmed as i32 { // Removing owner permission, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(org_id, UserOrgType::Owner, &mut conn).await <= 1 { + if UserOrganization::count_confirmed_by_org_and_type(org_id, UserOrgType::Owner, &conn).await <= 1 { err!("Can't delete the last owner") } } @@ -1355,7 +1325,7 @@ async fn edit_user( // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type // It returns different error messages per function. if new_type < UserOrgType::Admin { - match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, org_id, true, &mut conn).await { + match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, org_id, true, &conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { err!("You cannot modify this user to this type because it has no two-step login method activated"); @@ -1370,14 +1340,14 @@ async fn edit_user( user_to_edit.atype = new_type as i32; // Delete all the odd collections - for c in CollectionUser::find_by_organization_and_user_uuid(org_id, &user_to_edit.user_uuid, &mut conn).await { - c.delete(&mut conn).await?; + for c in CollectionUser::find_by_organization_and_user_uuid(org_id, &user_to_edit.user_uuid, &conn).await { + c.delete(&conn).await?; } // If no accessAll, add the collections received if !data.AccessAll { for col in data.Collections.iter().flatten() { - match Collection::find_by_uuid_and_org(&col.Id, org_id, &mut conn).await { + match Collection::find_by_uuid_and_org(&col.Id, org_id, &conn).await { None => err!("Collection not found in Organization"), Some(collection) => { CollectionUser::save( @@ -1385,7 +1355,7 @@ async fn edit_user( &collection.uuid, col.ReadOnly, col.HidePasswords, - &mut conn, + &conn, ) .await?; } @@ -1393,11 +1363,11 @@ async fn edit_user( } } - GroupUser::delete_all_by_user(&user_to_edit.uuid, &mut conn).await?; + GroupUser::delete_all_by_user(&user_to_edit.uuid, &conn).await?; for group in data.Groups.iter().flatten() { let mut group_entry = GroupUser::new(String::from(group), user_to_edit.uuid.clone()); - group_entry.save(&mut conn).await?; + group_entry.save(&conn).await?; } log_event( @@ -1407,11 +1377,11 @@ async fn edit_user( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - user_to_edit.save(&mut conn).await + user_to_edit.save(&conn).await } #[delete("/organizations//users", data = "")] @@ -1419,14 +1389,14 @@ async fn bulk_delete_user( org_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> Json { let data: OrgBulkIds = data.into_inner().data; let mut bulk_response = Vec::new(); for org_user_id in data.Ids { - let err_msg = match _delete_user(org_id, &org_user_id, &headers, &mut conn, &nt).await { + let err_msg = match _delete_user(org_id, &org_user_id, &headers, &conn, &nt).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1452,10 +1422,10 @@ async fn delete_user( org_id: &str, org_user_id: &str, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - _delete_user(org_id, org_user_id, &headers, &mut conn, &nt).await + _delete_user(org_id, org_user_id, &headers, &conn, &nt).await } #[post("/organizations//users//delete")] @@ -1463,17 +1433,17 @@ async fn post_delete_user( org_id: &str, org_user_id: &str, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - _delete_user(org_id, org_user_id, &headers, &mut conn, &nt).await + _delete_user(org_id, org_user_id, &headers, &conn, &nt).await } async fn _delete_user( org_id: &str, org_user_id: &str, headers: &AdminHeaders, - conn: &mut DbConn, + conn: &DbConn, nt: &Notify<'_>, ) -> EmptyResult { let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { @@ -1515,7 +1485,7 @@ async fn bulk_public_keys( org_id: &str, data: JsonUpcase, _headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> Json { let data: OrgBulkIds = data.into_inner().data; @@ -1524,8 +1494,8 @@ async fn bulk_public_keys( // If the user does not exists, just ignore it, and do not return any information regarding that UserOrg UUID. // The web-vault will then ignore that user for the following steps. for user_org_id in data.Ids { - match UserOrganization::find_by_uuid_and_org(&user_org_id, org_id, &mut conn).await { - Some(user_org) => match User::find_by_uuid(&user_org.user_uuid, &mut conn).await { + match UserOrganization::find_by_uuid_and_org(&user_org_id, org_id, &conn).await { + Some(user_org) => match User::find_by_uuid(&user_org.user_uuid, &conn).await { Some(user) => bulk_response.push(json!( { "Object": "organizationUserPublicKeyResponseModel", @@ -1572,7 +1542,7 @@ async fn post_org_import( query: OrgIdData, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let data: ImportData = data.into_inner().data; @@ -1587,7 +1557,7 @@ async fn post_org_import( let mut collections = Vec::new(); for coll in data.Collections { let collection = Collection::new(org_id.clone(), coll.Name, coll.ExternalId); - if collection.save(&mut conn).await.is_err() { + if collection.save(&conn).await.is_err() { collections.push(Err(Error::new("Failed to create Collection", "Failed to create Collection"))); } else { collections.push(Ok(collection)); @@ -1605,7 +1575,7 @@ async fn post_org_import( let mut ciphers = Vec::new(); for cipher_data in data.Ciphers { let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); - update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None).await.ok(); + update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None).await.ok(); ciphers.push(cipher); } @@ -1618,16 +1588,16 @@ async fn post_org_import( Err(_) => err!("Failed to assign to collection"), }; - CollectionCipher::save(cipher_id, coll_id, &mut conn).await?; + CollectionCipher::save(cipher_id, coll_id, &conn).await?; } let mut user = headers.user; - user.update_revision(&mut conn).await + user.update_revision(&conn).await } #[get("/organizations//policies")] -async fn list_policies(org_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> Json { - let policies = OrgPolicy::find_by_org(org_id, &mut conn).await; +async fn list_policies(org_id: &str, _headers: AdminHeaders, conn: DbConn) -> Json { + let policies = OrgPolicy::find_by_org(org_id, &conn).await; let policies_json: Vec = policies.iter().map(OrgPolicy::to_json).collect(); Json(json!({ @@ -1638,7 +1608,7 @@ async fn list_policies(org_id: &str, _headers: AdminHeaders, mut conn: DbConn) - } #[get("/organizations//policies/token?")] -async fn list_policies_token(org_id: &str, token: &str, mut conn: DbConn) -> JsonResult { +async fn list_policies_token(org_id: &str, token: &str, conn: DbConn) -> JsonResult { let invite = crate::auth::decode_invite(token)?; let invite_org_id = match invite.org_id { @@ -1651,7 +1621,7 @@ async fn list_policies_token(org_id: &str, token: &str, mut conn: DbConn) -> Jso } // TODO: We receive the invite token as ?token=<>, validate it contains the org id - let policies = OrgPolicy::find_by_org(org_id, &mut conn).await; + let policies = OrgPolicy::find_by_org(org_id, &conn).await; let policies_json: Vec = policies.iter().map(OrgPolicy::to_json).collect(); Ok(Json(json!({ @@ -1662,13 +1632,13 @@ async fn list_policies_token(org_id: &str, token: &str, mut conn: DbConn) -> Jso } #[get("/organizations//policies/")] -async fn get_policy(org_id: &str, pol_type: i32, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_policy(org_id: &str, pol_type: i32, _headers: AdminHeaders, conn: DbConn) -> JsonResult { let pol_type_enum = match OrgPolicyType::from_i32(pol_type) { Some(pt) => pt, None => err!("Invalid or unsupported policy type"), }; - let policy = match OrgPolicy::find_by_org_and_type(org_id, pol_type_enum, &mut conn).await { + let policy = match OrgPolicy::find_by_org_and_type(org_id, pol_type_enum, &conn).await { Some(p) => p, None => OrgPolicy::new(String::from(org_id), pol_type_enum, "null".to_string()), }; @@ -1690,7 +1660,7 @@ async fn put_policy( pol_type: i32, data: Json, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { let data: PolicyData = data.into_inner(); @@ -1701,8 +1671,8 @@ async fn put_policy( // When enabling the TwoFactorAuthentication policy, remove this org's members that do have 2FA if pol_type_enum == OrgPolicyType::TwoFactorAuthentication && data.enabled { - for member in UserOrganization::find_by_org(org_id, &mut conn).await.into_iter() { - let user_twofactor_disabled = TwoFactor::find_by_user(&member.user_uuid, &mut conn).await.is_empty(); + for member in UserOrganization::find_by_org(org_id, &conn).await.into_iter() { + let user_twofactor_disabled = TwoFactor::find_by_user(&member.user_uuid, &conn).await.is_empty(); // Policy only applies to non-Owner/non-Admin members who have accepted joining the org // Invited users still need to accept the invite and will get an error when they try to accept the invite. @@ -1711,8 +1681,8 @@ async fn put_policy( && member.status != UserOrgStatus::Invited as i32 { if CONFIG.mail_enabled() { - let org = Organization::find_by_uuid(&member.org_uuid, &mut conn).await.unwrap(); - let user = User::find_by_uuid(&member.user_uuid, &mut conn).await.unwrap(); + let org = Organization::find_by_uuid(&member.org_uuid, &conn).await.unwrap(); + let user = User::find_by_uuid(&member.user_uuid, &conn).await.unwrap(); mail::send_2fa_removed_from_org(&user.email, &org.name).await?; } @@ -1724,29 +1694,29 @@ async fn put_policy( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - member.delete(&mut conn).await?; + member.delete(&conn).await?; } } } // When enabling the SingleOrg policy, remove this org's members that are members of other orgs if pol_type_enum == OrgPolicyType::SingleOrg && data.enabled { - for member in UserOrganization::find_by_org(org_id, &mut conn).await.into_iter() { + for member in UserOrganization::find_by_org(org_id, &conn).await.into_iter() { // Policy only applies to non-Owner/non-Admin members who have accepted joining the org // Exclude invited and revoked users when checking for this policy. // Those users will not be allowed to accept or be activated because of the policy checks done there. // We check if the count is larger then 1, because it includes this organization also. if member.atype < UserOrgType::Admin && member.status != UserOrgStatus::Invited as i32 - && UserOrganization::count_accepted_and_confirmed_by_user(&member.user_uuid, &mut conn).await > 1 + && UserOrganization::count_accepted_and_confirmed_by_user(&member.user_uuid, &conn).await > 1 { if CONFIG.mail_enabled() { - let org = Organization::find_by_uuid(&member.org_uuid, &mut conn).await.unwrap(); - let user = User::find_by_uuid(&member.user_uuid, &mut conn).await.unwrap(); + let org = Organization::find_by_uuid(&member.org_uuid, &conn).await.unwrap(); + let user = User::find_by_uuid(&member.user_uuid, &conn).await.unwrap(); mail::send_single_org_removed_from_org(&user.email, &org.name).await?; } @@ -1758,23 +1728,23 @@ async fn put_policy( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - member.delete(&mut conn).await?; + member.delete(&conn).await?; } } } - let mut policy = match OrgPolicy::find_by_org_and_type(org_id, pol_type_enum, &mut conn).await { + let mut policy = match OrgPolicy::find_by_org_and_type(org_id, pol_type_enum, &conn).await { Some(p) => p, None => OrgPolicy::new(String::from(org_id), pol_type_enum, "{}".to_string()), }; policy.enabled = data.enabled; policy.data = serde_json::to_string(&data.data)?; - policy.save(&mut conn).await?; + policy.save(&conn).await?; log_event( EventType::PolicyUpdated as i32, @@ -1783,7 +1753,7 @@ async fn put_policy( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; @@ -1874,7 +1844,7 @@ struct OrgImportData { } #[post("/organizations//import", data = "")] -async fn import(org_id: &str, data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn import(org_id: &str, data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { let data = data.into_inner().data; // TODO: Currently we aren't storing the externalId's anywhere, so we also don't have a way @@ -1883,7 +1853,7 @@ async fn import(org_id: &str, data: JsonUpcase, headers: Headers, // as opposed to upstream which only removes auto-imported users. // User needs to be admin or owner to use the Directory Connector - match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { + match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &conn).await { Some(user_org) if user_org.atype >= UserOrgType::Admin => { /* Okay, nothing to do */ } Some(_) => err!("User has insufficient permissions to use Directory Connector"), None => err!("User not part of organization"), @@ -1892,7 +1862,7 @@ async fn import(org_id: &str, data: JsonUpcase, headers: Headers, for user_data in &data.Users { if user_data.Deleted { // If user is marked for deletion and it exists, delete it - if let Some(user_org) = UserOrganization::find_by_email_and_org(&user_data.Email, org_id, &mut conn).await { + if let Some(user_org) = UserOrganization::find_by_email_and_org(&user_data.Email, org_id, &conn).await { log_event( EventType::OrganizationUserRemoved as i32, &user_org.uuid, @@ -1900,16 +1870,16 @@ async fn import(org_id: &str, data: JsonUpcase, headers: Headers, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - user_org.delete(&mut conn).await?; + user_org.delete(&conn).await?; } // If user is not part of the organization, but it exists - } else if UserOrganization::find_by_email_and_org(&user_data.Email, org_id, &mut conn).await.is_none() { - if let Some(user) = User::find_by_mail(&user_data.Email, &mut conn).await { + } else if UserOrganization::find_by_email_and_org(&user_data.Email, org_id, &conn).await.is_none() { + if let Some(user) = User::find_by_mail(&user_data.Email, &conn).await { let user_org_status = if CONFIG.mail_enabled() { UserOrgStatus::Invited as i32 } else { @@ -1921,7 +1891,7 @@ async fn import(org_id: &str, data: JsonUpcase, headers: Headers, new_org_user.atype = UserOrgType::User as i32; new_org_user.status = user_org_status; - new_org_user.save(&mut conn).await?; + new_org_user.save(&conn).await?; log_event( EventType::OrganizationUserInvited as i32, @@ -1930,12 +1900,12 @@ async fn import(org_id: &str, data: JsonUpcase, headers: Headers, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; if CONFIG.mail_enabled() { - let org_name = match Organization::find_by_uuid(org_id, &mut conn).await { + let org_name = match Organization::find_by_uuid(org_id, &conn).await { Some(org) => org.name, None => err!("Error looking up organization"), }; @@ -1956,8 +1926,8 @@ async fn import(org_id: &str, data: JsonUpcase, headers: Headers, // If this flag is enabled, any user that isn't provided in the Users list will be removed (by default they will be kept unless they have Deleted == true) if data.OverwriteExisting { - for user_org in UserOrganization::find_by_org_and_type(org_id, UserOrgType::User, &mut conn).await { - if let Some(user_email) = User::find_by_uuid(&user_org.user_uuid, &mut conn).await.map(|u| u.email) { + for user_org in UserOrganization::find_by_org_and_type(org_id, UserOrgType::User, &conn).await { + if let Some(user_email) = User::find_by_uuid(&user_org.user_uuid, &conn).await.map(|u| u.email) { if !data.Users.iter().any(|u| u.Email == user_email) { log_event( EventType::OrganizationUserRemoved as i32, @@ -1966,11 +1936,11 @@ async fn import(org_id: &str, data: JsonUpcase, headers: Headers, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - user_org.delete(&mut conn).await?; + user_org.delete(&conn).await?; } } } @@ -1985,9 +1955,9 @@ async fn deactivate_organization_user( org_id: &str, org_user_id: &str, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { - _revoke_organization_user(org_id, org_user_id, &headers, &mut conn).await + _revoke_organization_user(org_id, org_user_id, &headers, &conn).await } // Pre web-vault v2022.9.x endpoint @@ -2002,13 +1972,8 @@ async fn bulk_deactivate_organization_user( } #[put("/organizations//users//revoke")] -async fn revoke_organization_user( - org_id: &str, - org_user_id: &str, - headers: AdminHeaders, - mut conn: DbConn, -) -> EmptyResult { - _revoke_organization_user(org_id, org_user_id, &headers, &mut conn).await +async fn revoke_organization_user(org_id: &str, org_user_id: &str, headers: AdminHeaders, conn: DbConn) -> EmptyResult { + _revoke_organization_user(org_id, org_user_id, &headers, &conn).await } #[put("/organizations//users/revoke", data = "")] @@ -2016,7 +1981,7 @@ async fn bulk_revoke_organization_user( org_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> Json { let data = data.into_inner().data; @@ -2025,7 +1990,7 @@ async fn bulk_revoke_organization_user( Some(org_users) => { for org_user_id in org_users { let org_user_id = org_user_id.as_str().unwrap_or_default(); - let err_msg = match _revoke_organization_user(org_id, org_user_id, &headers, &mut conn).await { + let err_msg = match _revoke_organization_user(org_id, org_user_id, &headers, &conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2053,7 +2018,7 @@ async fn _revoke_organization_user( org_id: &str, org_user_id: &str, headers: &AdminHeaders, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { Some(mut user_org) if user_org.status > UserOrgStatus::Revoked as i32 => { @@ -2095,9 +2060,9 @@ async fn activate_organization_user( org_id: &str, org_user_id: &str, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { - _restore_organization_user(org_id, org_user_id, &headers, &mut conn).await + _restore_organization_user(org_id, org_user_id, &headers, &conn).await } // Pre web-vault v2022.9.x endpoint @@ -2116,9 +2081,9 @@ async fn restore_organization_user( org_id: &str, org_user_id: &str, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { - _restore_organization_user(org_id, org_user_id, &headers, &mut conn).await + _restore_organization_user(org_id, org_user_id, &headers, &conn).await } #[put("/organizations//users/restore", data = "")] @@ -2126,7 +2091,7 @@ async fn bulk_restore_organization_user( org_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> Json { let data = data.into_inner().data; @@ -2135,7 +2100,7 @@ async fn bulk_restore_organization_user( Some(org_users) => { for org_user_id in org_users { let org_user_id = org_user_id.as_str().unwrap_or_default(); - let err_msg = match _restore_organization_user(org_id, org_user_id, &headers, &mut conn).await { + let err_msg = match _restore_organization_user(org_id, org_user_id, &headers, &conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2163,7 +2128,7 @@ async fn _restore_organization_user( org_id: &str, org_user_id: &str, headers: &AdminHeaders, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { Some(mut user_org) if user_org.status < UserOrgStatus::Accepted as i32 => { @@ -2209,13 +2174,13 @@ async fn _restore_organization_user( } #[get("/organizations//groups")] -async fn get_groups(org_id: &str, _headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { +async fn get_groups(org_id: &str, _headers: ManagerHeadersLoose, conn: DbConn) -> JsonResult { let groups: Vec = if CONFIG.org_groups_enabled() { - // Group::find_by_organization(&org_id, &mut conn).await.iter().map(Group::to_json).collect::() - let groups = Group::find_by_organization(org_id, &mut conn).await; + // Group::find_by_organization(&org_id, &conn).await.iter().map(Group::to_json).collect::() + let groups = Group::find_by_organization(org_id, &conn).await; let mut groups_json = Vec::with_capacity(groups.len()); for g in groups { - groups_json.push(g.to_json_details(&mut conn).await) + groups_json.push(g.to_json_details(&conn).await) } groups_json } else { @@ -2307,12 +2272,7 @@ async fn post_group( } #[post("/organizations//groups", data = "")] -async fn post_groups( - org_id: &str, - headers: AdminHeaders, - data: JsonUpcase, - mut conn: DbConn, -) -> JsonResult { +async fn post_groups(org_id: &str, headers: AdminHeaders, data: JsonUpcase, conn: DbConn) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } @@ -2327,11 +2287,11 @@ async fn post_groups( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - add_update_group(group, group_request.Collections, group_request.Users, org_id, &headers, &mut conn).await + add_update_group(group, group_request.Collections, group_request.Users, org_id, &headers, &conn).await } #[put("/organizations//groups/", data = "")] @@ -2340,13 +2300,13 @@ async fn put_group( group_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - let group = match Group::find_by_uuid(group_id, &mut conn).await { + let group = match Group::find_by_uuid(group_id, &conn).await { Some(group) => group, None => err!("Group not found"), }; @@ -2354,8 +2314,8 @@ async fn put_group( let group_request = data.into_inner().data; let updated_group = group_request.update_group(group); - CollectionGroup::delete_all_by_group(group_id, &mut conn).await?; - GroupUser::delete_all_by_group(group_id, &mut conn).await?; + CollectionGroup::delete_all_by_group(group_id, &conn).await?; + GroupUser::delete_all_by_group(group_id, &conn).await?; log_event( EventType::GroupUpdated as i32, @@ -2364,11 +2324,11 @@ async fn put_group( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - add_update_group(updated_group, group_request.Collections, group_request.Users, org_id, &headers, &mut conn).await + add_update_group(updated_group, group_request.Collections, group_request.Users, org_id, &headers, &conn).await } async fn add_update_group( @@ -2377,7 +2337,7 @@ async fn add_update_group( users: Vec, org_id: &str, headers: &AdminHeaders, - conn: &mut DbConn, + conn: &DbConn, ) -> JsonResult { group.save(conn).await?; @@ -2412,30 +2372,30 @@ async fn add_update_group( } #[get("/organizations/<_org_id>/groups//details")] -async fn get_group_details(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_group_details(_org_id: &str, group_id: &str, _headers: AdminHeaders, conn: DbConn) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - let group = match Group::find_by_uuid(group_id, &mut conn).await { + let group = match Group::find_by_uuid(group_id, &conn).await { Some(group) => group, _ => err!("Group could not be found!"), }; - Ok(Json(group.to_json_details(&mut conn).await)) + Ok(Json(group.to_json_details(&conn).await)) } #[post("/organizations//groups//delete")] -async fn post_delete_group(org_id: &str, group_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { - _delete_group(org_id, group_id, &headers, &mut conn).await +async fn post_delete_group(org_id: &str, group_id: &str, headers: AdminHeaders, conn: DbConn) -> EmptyResult { + _delete_group(org_id, group_id, &headers, &conn).await } #[delete("/organizations//groups/")] -async fn delete_group(org_id: &str, group_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { - _delete_group(org_id, group_id, &headers, &mut conn).await +async fn delete_group(org_id: &str, group_id: &str, headers: AdminHeaders, conn: DbConn) -> EmptyResult { + _delete_group(org_id, group_id, &headers, &conn).await } -async fn _delete_group(org_id: &str, group_id: &str, headers: &AdminHeaders, conn: &mut DbConn) -> EmptyResult { +async fn _delete_group(org_id: &str, group_id: &str, headers: &AdminHeaders, conn: &DbConn) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } @@ -2464,7 +2424,7 @@ async fn bulk_delete_groups( org_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2473,18 +2433,18 @@ async fn bulk_delete_groups( let data: OrgBulkIds = data.into_inner().data; for group_id in data.Ids { - _delete_group(org_id, &group_id, &headers, &mut conn).await? + _delete_group(org_id, &group_id, &headers, &conn).await? } Ok(()) } #[get("/organizations/<_org_id>/groups/")] -async fn get_group(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_group(_org_id: &str, group_id: &str, _headers: AdminHeaders, conn: DbConn) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - let group = match Group::find_by_uuid(group_id, &mut conn).await { + let group = match Group::find_by_uuid(group_id, &conn).await { Some(group) => group, _ => err!("Group not found"), }; @@ -2493,17 +2453,17 @@ async fn get_group(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut co } #[get("/organizations/<_org_id>/groups//users")] -async fn get_group_users(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_group_users(_org_id: &str, group_id: &str, _headers: AdminHeaders, conn: DbConn) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - match Group::find_by_uuid(group_id, &mut conn).await { + match Group::find_by_uuid(group_id, &conn).await { Some(_) => { /* Do nothing */ } _ => err!("Group could not be found!"), }; - let group_users: Vec = GroupUser::find_by_group(group_id, &mut conn) + let group_users: Vec = GroupUser::find_by_group(group_id, &conn) .await .iter() .map(|entry| entry.users_organizations_uuid.clone()) @@ -2518,23 +2478,23 @@ async fn put_group_users( group_id: &str, headers: AdminHeaders, data: JsonVec, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - match Group::find_by_uuid(group_id, &mut conn).await { + match Group::find_by_uuid(group_id, &conn).await { Some(_) => { /* Do nothing */ } _ => err!("Group could not be found!"), }; - GroupUser::delete_all_by_group(group_id, &mut conn).await?; + GroupUser::delete_all_by_group(group_id, &conn).await?; let assigned_user_ids = data.into_inner(); for assigned_user_id in assigned_user_ids { let mut user_entry = GroupUser::new(String::from(group_id), assigned_user_id.clone()); - user_entry.save(&mut conn).await?; + user_entry.save(&conn).await?; log_event( EventType::OrganizationUserUpdatedGroups as i32, @@ -2543,7 +2503,7 @@ async fn put_group_users( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; } @@ -2552,18 +2512,18 @@ async fn put_group_users( } #[get("/organizations/<_org_id>/users//groups")] -async fn get_user_groups(_org_id: &str, user_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_user_groups(_org_id: &str, user_id: &str, _headers: AdminHeaders, conn: DbConn) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - match UserOrganization::find_by_uuid(user_id, &mut conn).await { + match UserOrganization::find_by_uuid(user_id, &conn).await { Some(_) => { /* Do nothing */ } _ => err!("User could not be found!"), }; let user_groups: Vec = - GroupUser::find_by_user(user_id, &mut conn).await.iter().map(|entry| entry.groups_uuid.clone()).collect(); + GroupUser::find_by_user(user_id, &conn).await.iter().map(|entry| entry.groups_uuid.clone()).collect(); Ok(Json(json!(user_groups))) } @@ -2591,13 +2551,13 @@ async fn put_user_groups( org_user_id: &str, data: JsonUpcase, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - let user_org = match UserOrganization::find_by_uuid(org_user_id, &mut conn).await { + let user_org = match UserOrganization::find_by_uuid(org_user_id, &conn).await { Some(uo) => uo, _ => err!("User could not be found!"), }; @@ -2606,12 +2566,12 @@ async fn put_user_groups( err!("Group doesn't belong to organization"); } - GroupUser::delete_all_by_user(org_user_id, &mut conn).await?; + GroupUser::delete_all_by_user(org_user_id, &conn).await?; let assigned_group_ids = data.into_inner().data; for assigned_group_id in assigned_group_ids.GroupIds { let mut group_user = GroupUser::new(assigned_group_id.clone(), String::from(org_user_id)); - group_user.save(&mut conn).await?; + group_user.save(&conn).await?; } log_event( @@ -2621,7 +2581,7 @@ async fn put_user_groups( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; @@ -2645,13 +2605,13 @@ async fn delete_group_user( group_id: &str, org_user_id: &str, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } - let user_org = match UserOrganization::find_by_uuid(org_user_id, &mut conn).await { + let user_org = match UserOrganization::find_by_uuid(org_user_id, &conn).await { Some(uo) => uo, _ => err!("User could not be found!"), }; @@ -2660,7 +2620,7 @@ async fn delete_group_user( err!("User doesn't belong to organization"); } - let group = match Group::find_by_uuid(group_id, &mut conn).await { + let group = match Group::find_by_uuid(group_id, &conn).await { Some(g) => g, _ => err!("Group could not be found!"), }; @@ -2676,11 +2636,11 @@ async fn delete_group_user( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; - GroupUser::delete_by_group_id_and_user_id(group_id, org_user_id, &mut conn).await + GroupUser::delete_by_group_id_and_user_id(group_id, org_user_id, &conn).await } #[derive(Deserialize)] @@ -2698,8 +2658,8 @@ struct OrganizationUserResetPasswordRequest { } #[get("/organizations//keys")] -async fn get_organization_keys(org_id: &str, mut conn: DbConn) -> JsonResult { - let org = match Organization::find_by_uuid(org_id, &mut conn).await { +async fn get_organization_keys(org_id: &str, conn: DbConn) -> JsonResult { + let org = match Organization::find_by_uuid(org_id, &conn).await { Some(organization) => organization, None => err!("Organization not found"), }; @@ -2717,25 +2677,25 @@ async fn put_reset_password( org_user_id: &str, headers: AdminHeaders, data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - let org = match Organization::find_by_uuid(org_id, &mut conn).await { + let org = match Organization::find_by_uuid(org_id, &conn).await { Some(org) => org, None => err!("Required organization not found"), }; - let org_user = match UserOrganization::find_by_uuid_and_org(org_user_id, &org.uuid, &mut conn).await { + let org_user = match UserOrganization::find_by_uuid_and_org(org_user_id, &org.uuid, &conn).await { Some(user) => user, None => err!("User to reset isn't member of required organization"), }; - let user = match User::find_by_uuid(&org_user.user_uuid, &mut conn).await { + let user = match User::find_by_uuid(&org_user.user_uuid, &conn).await { Some(user) => user, None => err!("User not found"), }; - check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &mut conn).await?; + check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &conn).await?; if org_user.reset_password_key.is_none() { err!("Password reset not or not correctly enrolled"); @@ -2754,7 +2714,7 @@ async fn put_reset_password( let mut user = user; user.set_password(reset_request.NewMasterPasswordHash.as_str(), Some(reset_request.Key), true, None); - user.save(&mut conn).await?; + user.save(&conn).await?; nt.send_logout(&user, None).await; @@ -2765,7 +2725,7 @@ async fn put_reset_password( headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, - &mut conn, + &conn, ) .await; @@ -2777,24 +2737,24 @@ async fn get_reset_password_details( org_id: &str, org_user_id: &str, headers: AdminHeaders, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { - let org = match Organization::find_by_uuid(org_id, &mut conn).await { + let org = match Organization::find_by_uuid(org_id, &conn).await { Some(org) => org, None => err!("Required organization not found"), }; - let org_user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { + let org_user = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &conn).await { Some(user) => user, None => err!("User to reset isn't member of required organization"), }; - let user = match User::find_by_uuid(&org_user.user_uuid, &mut conn).await { + let user = match User::find_by_uuid(&org_user.user_uuid, &conn).await { Some(user) => user, None => err!("User not found"), }; - check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &mut conn).await?; + check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &conn).await?; // https://github.com/bitwarden/server/blob/3b50ccb9f804efaacdc46bed5b60e5b28eddefcf/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs#L111 Ok(Json(json!({ @@ -2813,7 +2773,7 @@ async fn check_reset_password_applicable_and_permissions( org_id: &str, org_user_id: &str, headers: &AdminHeaders, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { check_reset_password_applicable(org_id, conn).await?; @@ -2830,7 +2790,7 @@ async fn check_reset_password_applicable_and_permissions( } } -async fn check_reset_password_applicable(org_id: &str, conn: &mut DbConn) -> EmptyResult { +async fn check_reset_password_applicable(org_id: &str, conn: &DbConn) -> EmptyResult { if !CONFIG.mail_enabled() { err!("Password reset is not supported on an email-disabled instance."); } @@ -2853,19 +2813,18 @@ async fn put_reset_password_enrollment( org_user_id: &str, headers: Headers, data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, ) -> EmptyResult { - let mut org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { + let mut org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &conn).await { Some(u) => u, None => err!("User to enroll isn't member of required organization"), }; - check_reset_password_applicable(org_id, &mut conn).await?; + check_reset_password_applicable(org_id, &conn).await?; let reset_request = data.into_inner().data; - if reset_request.ResetPasswordKey.is_none() && OrgPolicy::org_is_reset_password_auto_enroll(org_id, &mut conn).await - { + if reset_request.ResetPasswordKey.is_none() && OrgPolicy::org_is_reset_password_auto_enroll(org_id, &conn).await { err!("Reset password can't be withdrawed due to an enterprise policy"); } @@ -2881,7 +2840,7 @@ async fn put_reset_password_enrollment( } org_user.reset_password_key = reset_request.ResetPasswordKey; - org_user.save(&mut conn).await?; + org_user.save(&conn).await?; let log_id = if org_user.reset_password_key.is_some() { EventType::OrganizationUserResetPasswordEnroll as i32 @@ -2889,7 +2848,7 @@ async fn put_reset_password_enrollment( EventType::OrganizationUserResetPasswordWithdraw as i32 }; - log_event(log_id, org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, &mut conn) + log_event(log_id, org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, &conn) .await; Ok(()) @@ -2903,7 +2862,7 @@ async fn put_reset_password_enrollment( // We need to convert all keys so they have the first character to be a lowercase. // Else the export will be just an empty JSON file. #[get("/organizations//export")] -async fn get_org_export(org_id: &str, headers: AdminHeaders, mut conn: DbConn) -> Json { +async fn get_org_export(org_id: &str, headers: AdminHeaders, conn: DbConn) -> Json { use semver::{Version, VersionReq}; // Since version v2023.1.0 the format of the export is different. @@ -2924,12 +2883,12 @@ async fn get_org_export(org_id: &str, headers: AdminHeaders, mut conn: DbConn) - // Backwards compatible pre v2023.1.0 response Json(json!({ "collections": { - "data": convert_json_key_lcase_first(_get_org_collections(org_id, &mut conn).await), + "data": convert_json_key_lcase_first(_get_org_collections(org_id, &conn).await), "object": "list", "continuationToken": null, }, "ciphers": { - "data": convert_json_key_lcase_first(_get_org_details(org_id, &headers.host, &headers.user.uuid, &mut conn).await), + "data": convert_json_key_lcase_first(_get_org_details(org_id, &headers.host, &headers.user.uuid, &conn).await), "object": "list", "continuationToken": null, } @@ -2937,8 +2896,8 @@ async fn get_org_export(org_id: &str, headers: AdminHeaders, mut conn: DbConn) - } else { // v2023.1.0 and newer response Json(json!({ - "collections": convert_json_key_lcase_first(_get_org_collections(org_id, &mut conn).await), - "ciphers": convert_json_key_lcase_first(_get_org_details(org_id, &headers.host, &headers.user.uuid, &mut conn).await), + "collections": convert_json_key_lcase_first(_get_org_collections(org_id, &conn).await), + "ciphers": convert_json_key_lcase_first(_get_org_details(org_id, &headers.host, &headers.user.uuid, &conn).await), })) } } diff --git a/src/api/core/public.rs b/src/api/core/public.rs index 74f79a3e..bc1367ef 100644 --- a/src/api/core/public.rs +++ b/src/api/core/public.rs @@ -43,7 +43,7 @@ struct OrgImportData { } #[post("/public/organization/import", data = "")] -async fn ldap_import(data: JsonUpcase, token: PublicToken, mut conn: DbConn) -> EmptyResult { +async fn ldap_import(data: JsonUpcase, token: PublicToken, conn: DbConn) -> EmptyResult { // Most of the logic for this function can be found here // https://github.com/bitwarden/server/blob/fd892b2ff4547648a276734fb2b14a8abae2c6f5/src/Core/Services/Implementations/OrganizationService.cs#L1797 @@ -53,15 +53,13 @@ async fn ldap_import(data: JsonUpcase, token: PublicToken, mut co for user_data in &data.Members { if user_data.Deleted { // If user is marked for deletion and it exists, revoke it - if let Some(mut user_org) = - UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &mut conn).await + if let Some(mut user_org) = UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn).await { // Only revoke a user if it is not the last confirmed owner let revoked = if user_org.atype == UserOrgType::Owner && user_org.status == UserOrgStatus::Confirmed as i32 { - if UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &mut conn).await - <= 1 + if UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &conn).await <= 1 { warn!("Can't revoke the last owner"); false @@ -74,30 +72,30 @@ async fn ldap_import(data: JsonUpcase, token: PublicToken, mut co let ext_modified = user_org.set_external_id(Some(user_data.ExternalId.clone())); if revoked || ext_modified { - user_org.save(&mut conn).await?; + user_org.save(&conn).await?; } } // If user is part of the organization, restore it } else if let Some(mut user_org) = - UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &mut conn).await + UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn).await { let restored = user_org.restore(); let ext_modified = user_org.set_external_id(Some(user_data.ExternalId.clone())); if restored || ext_modified { - user_org.save(&mut conn).await?; + user_org.save(&conn).await?; } } else { // If user is not part of the organization - let user = match User::find_by_mail(&user_data.Email, &mut conn).await { + let user = match User::find_by_mail(&user_data.Email, &conn).await { Some(user) => user, // exists in vaultwarden None => { // User does not exist yet let mut new_user = User::new(user_data.Email.clone()); - new_user.save(&mut conn).await?; + new_user.save(&conn).await?; if !CONFIG.mail_enabled() { let invitation = Invitation::new(&new_user.email); - invitation.save(&mut conn).await?; + invitation.save(&conn).await?; } new_user } @@ -114,10 +112,10 @@ async fn ldap_import(data: JsonUpcase, token: PublicToken, mut co new_org_user.atype = UserOrgType::User as i32; new_org_user.status = user_org_status; - new_org_user.save(&mut conn).await?; + new_org_user.save(&conn).await?; if CONFIG.mail_enabled() { - let (org_name, org_email) = match Organization::find_by_uuid(&org_id, &mut conn).await { + let (org_name, org_email) = match Organization::find_by_uuid(&org_id, &conn).await { Some(org) => (org.name, org.billing_email), None => err!("Error looking up organization"), }; @@ -137,23 +135,22 @@ async fn ldap_import(data: JsonUpcase, token: PublicToken, mut co if CONFIG.org_groups_enabled() { for group_data in &data.Groups { - let group_uuid = match Group::find_by_external_id(&group_data.ExternalId, &mut conn).await { + let group_uuid = match Group::find_by_external_id(&group_data.ExternalId, &conn).await { Some(group) => group.uuid, None => { let mut group = Group::new(org_id.clone(), group_data.Name.clone(), false, Some(group_data.ExternalId.clone())); - group.save(&mut conn).await?; + group.save(&conn).await?; group.uuid } }; - GroupUser::delete_all_by_group(&group_uuid, &mut conn).await?; + GroupUser::delete_all_by_group(&group_uuid, &conn).await?; for ext_id in &group_data.MemberExternalIds { - if let Some(user_org) = UserOrganization::find_by_external_id_and_org(ext_id, &org_id, &mut conn).await - { + if let Some(user_org) = UserOrganization::find_by_external_id_and_org(ext_id, &org_id, &conn).await { let mut group_user = GroupUser::new(group_uuid.clone(), user_org.uuid.clone()); - group_user.save(&mut conn).await?; + group_user.save(&conn).await?; } } } @@ -165,20 +162,19 @@ async fn ldap_import(data: JsonUpcase, token: PublicToken, mut co if data.OverwriteExisting { // Generate a HashSet to quickly verify if a member is listed or not. let sync_members: HashSet = data.Members.into_iter().map(|m| m.ExternalId).collect(); - for user_org in UserOrganization::find_by_org(&org_id, &mut conn).await { + for user_org in UserOrganization::find_by_org(&org_id, &conn).await { if let Some(ref user_external_id) = user_org.external_id { if !sync_members.contains(user_external_id) { if user_org.atype == UserOrgType::Owner && user_org.status == UserOrgStatus::Confirmed as i32 { // Removing owner, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &mut conn) - .await + if UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &conn).await <= 1 { warn!("Can't delete the last owner"); continue; } } - user_org.delete(&mut conn).await?; + user_org.delete(&conn).await?; } } } diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs index cf6b60ff..1615912d 100644 --- a/src/api/core/sends.rs +++ b/src/api/core/sends.rs @@ -39,8 +39,8 @@ pub fn routes() -> Vec { pub async fn purge_sends(pool: DbPool) { debug!("Purging sends"); - if let Ok(mut conn) = pool.get().await { - Send::purge(&mut conn).await; + if let Ok(conn) = pool.get().await { + Send::purge(&conn).await; } else { error!("Failed to get DB connection while purging sends") } @@ -74,7 +74,7 @@ struct SendData { /// /// There is also a Vaultwarden-specific `sends_allowed` config setting that /// controls this policy globally. -async fn enforce_disable_send_policy(headers: &Headers, conn: &mut DbConn) -> EmptyResult { +async fn enforce_disable_send_policy(headers: &Headers, conn: &DbConn) -> EmptyResult { let user_uuid = &headers.user.uuid; if !CONFIG.sends_allowed() || OrgPolicy::is_applicable_to_user(user_uuid, OrgPolicyType::DisableSend, None, conn).await @@ -90,7 +90,7 @@ async fn enforce_disable_send_policy(headers: &Headers, conn: &mut DbConn) -> Em /// but is allowed to remove this option from an existing Send. /// /// Ref: https://bitwarden.com/help/article/policies/#send-options -async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &mut DbConn) -> EmptyResult { +async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &DbConn) -> EmptyResult { let user_uuid = &headers.user.uuid; let hide_email = data.HideEmail.unwrap_or(false); if hide_email && OrgPolicy::is_hide_email_disabled(user_uuid, conn).await { @@ -142,8 +142,8 @@ fn create_send(data: SendData, user_uuid: String) -> ApiResult { } #[get("/sends")] -async fn get_sends(headers: Headers, mut conn: DbConn) -> Json { - let sends = Send::find_by_user(&headers.user.uuid, &mut conn); +async fn get_sends(headers: Headers, conn: DbConn) -> Json { + let sends = Send::find_by_user(&headers.user.uuid, &conn); let sends_json: Vec = sends.await.iter().map(|s| s.to_json()).collect(); Json(json!({ @@ -154,8 +154,8 @@ async fn get_sends(headers: Headers, mut conn: DbConn) -> Json { } #[get("/sends/")] -async fn get_send(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { - let send = match Send::find_by_uuid(uuid, &mut conn).await { +async fn get_send(uuid: &str, headers: Headers, conn: DbConn) -> JsonResult { + let send = match Send::find_by_uuid(uuid, &conn).await { Some(send) => send, None => err!("Send not found"), }; @@ -168,24 +168,24 @@ async fn get_send(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult } #[post("/sends", data = "")] -async fn post_send(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { - enforce_disable_send_policy(&headers, &mut conn).await?; +async fn post_send(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { + enforce_disable_send_policy(&headers, &conn).await?; let data: SendData = data.into_inner().data; - enforce_disable_hide_email_policy(&data, &headers, &mut conn).await?; + enforce_disable_hide_email_policy(&data, &headers, &conn).await?; if data.Type == SendType::File as i32 { err!("File sends should use /api/sends/file") } let mut send = create_send(data, headers.user.uuid)?; - send.save(&mut conn).await?; + send.save(&conn).await?; nt.send_send_update( UpdateType::SyncSendCreate, &send, - &send.update_users_revision(&mut conn).await, + &send.update_users_revision(&conn).await, &headers.device.uuid, - &mut conn, + &conn, ) .await; @@ -207,8 +207,8 @@ struct UploadDataV2<'f> { // This method still exists to support older clients, probably need to remove it sometime. // Upstream: https://github.com/bitwarden/server/blob/d0c793c95181dfb1b447eb450f85ba0bfd7ef643/src/Api/Controllers/SendsController.cs#L164-L167 #[post("/sends/file", format = "multipart/form-data", data = "")] -async fn post_send_file(data: Form>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { - enforce_disable_send_policy(&headers, &mut conn).await?; +async fn post_send_file(data: Form>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { + enforce_disable_send_policy(&headers, &conn).await?; let UploadData { model, @@ -216,12 +216,12 @@ async fn post_send_file(data: Form>, headers: Headers, mut conn: } = data.into_inner(); let model = model.into_inner().data; - enforce_disable_hide_email_policy(&model, &headers, &mut conn).await?; + enforce_disable_hide_email_policy(&model, &headers, &conn).await?; let size_limit = match CONFIG.user_attachment_limit() { Some(0) => err!("File uploads are disabled"), Some(limit_kb) => { - let left = (limit_kb * 1024) - Attachment::size_by_user(&headers.user.uuid, &mut conn).await; + let left = (limit_kb * 1024) - Attachment::size_by_user(&headers.user.uuid, &conn).await; if left <= 0 { err!("Attachment storage limit reached! Delete some attachments to free up space") } @@ -258,13 +258,13 @@ async fn post_send_file(data: Form>, headers: Headers, mut conn: send.data = serde_json::to_string(&data_value)?; // Save the changes in the database - send.save(&mut conn).await?; + send.save(&conn).await?; nt.send_send_update( UpdateType::SyncSendCreate, &send, - &send.update_users_revision(&mut conn).await, + &send.update_users_revision(&conn).await, &headers.device.uuid, - &mut conn, + &conn, ) .await; @@ -273,8 +273,8 @@ async fn post_send_file(data: Form>, headers: Headers, mut conn: // Upstream: https://github.com/bitwarden/server/blob/d0c793c95181dfb1b447eb450f85ba0bfd7ef643/src/Api/Controllers/SendsController.cs#L190 #[post("/sends/file/v2", data = "")] -async fn post_send_file_v2(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { - enforce_disable_send_policy(&headers, &mut conn).await?; +async fn post_send_file_v2(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { + enforce_disable_send_policy(&headers, &conn).await?; let data = data.into_inner().data; @@ -282,7 +282,7 @@ async fn post_send_file_v2(data: JsonUpcase, headers: Headers, mut con err!("Send content is not a file"); } - enforce_disable_hide_email_policy(&data, &headers, &mut conn).await?; + enforce_disable_hide_email_policy(&data, &headers, &conn).await?; let file_length = match &data.FileLength { Some(m) => Some(m.into_i32()?), @@ -292,7 +292,7 @@ async fn post_send_file_v2(data: JsonUpcase, headers: Headers, mut con let size_limit = match CONFIG.user_attachment_limit() { Some(0) => err!("File uploads are disabled"), Some(limit_kb) => { - let left = (limit_kb * 1024) - Attachment::size_by_user(&headers.user.uuid, &mut conn).await; + let left = (limit_kb * 1024) - Attachment::size_by_user(&headers.user.uuid, &conn).await; if left <= 0 { err!("Attachment storage limit reached! Delete some attachments to free up space") } @@ -316,7 +316,7 @@ async fn post_send_file_v2(data: JsonUpcase, headers: Headers, mut con o.insert(String::from("SizeName"), Value::String(crate::util::get_display_size(file_length.unwrap()))); } send.data = serde_json::to_string(&data_value)?; - send.save(&mut conn).await?; + send.save(&conn).await?; Ok(Json(json!({ "fileUploadType": 0, // 0 == Direct | 1 == Azure @@ -333,14 +333,14 @@ async fn post_send_file_v2_data( file_id: &str, data: Form>, headers: Headers, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - enforce_disable_send_policy(&headers, &mut conn).await?; + enforce_disable_send_policy(&headers, &conn).await?; let mut data = data.into_inner(); - let Some(send) = Send::find_by_uuid(send_uuid, &mut conn).await else { + let Some(send) = Send::find_by_uuid(send_uuid, &conn).await else { err!("Send not found. Unable to save the file.") }; @@ -362,9 +362,9 @@ async fn post_send_file_v2_data( nt.send_send_update( UpdateType::SyncSendCreate, &send, - &send.update_users_revision(&mut conn).await, + &send.update_users_revision(&conn).await, &headers.device.uuid, - &mut conn, + &conn, ) .await; @@ -381,11 +381,11 @@ pub struct SendAccessData { async fn post_access( access_id: &str, data: JsonUpcase, - mut conn: DbConn, + conn: DbConn, ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - let mut send = match Send::find_by_access_id(access_id, &mut conn).await { + let mut send = match Send::find_by_access_id(access_id, &conn).await { Some(s) => s, None => err_code!(SEND_INACCESSIBLE_MSG, 404), }; @@ -423,18 +423,18 @@ async fn post_access( send.access_count += 1; } - send.save(&mut conn).await?; + send.save(&conn).await?; nt.send_send_update( UpdateType::SyncSendUpdate, &send, - &send.update_users_revision(&mut conn).await, + &send.update_users_revision(&conn).await, &String::from("00000000-0000-0000-0000-000000000000"), - &mut conn, + &conn, ) .await; - Ok(Json(send.to_json_access(&mut conn).await)) + Ok(Json(send.to_json_access(&conn).await)) } #[post("/sends//access/file/", data = "")] @@ -443,10 +443,10 @@ async fn post_access_file( file_id: &str, data: JsonUpcase, host: Host, - mut conn: DbConn, + conn: DbConn, nt: Notify<'_>, ) -> JsonResult { - let mut send = match Send::find_by_uuid(send_id, &mut conn).await { + let mut send = match Send::find_by_uuid(send_id, &conn).await { Some(s) => s, None => err_code!(SEND_INACCESSIBLE_MSG, 404), }; @@ -481,14 +481,14 @@ async fn post_access_file( send.access_count += 1; - send.save(&mut conn).await?; + send.save(&conn).await?; nt.send_send_update( UpdateType::SyncSendUpdate, &send, - &send.update_users_revision(&mut conn).await, + &send.update_users_revision(&conn).await, &String::from("00000000-0000-0000-0000-000000000000"), - &mut conn, + &conn, ) .await; @@ -512,19 +512,13 @@ async fn download_send(send_id: SafeString, file_id: SafeString, t: &str) -> Opt } #[put("/sends/", data = "")] -async fn put_send( - id: &str, - data: JsonUpcase, - headers: Headers, - mut conn: DbConn, - nt: Notify<'_>, -) -> JsonResult { - enforce_disable_send_policy(&headers, &mut conn).await?; +async fn put_send(id: &str, data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { + enforce_disable_send_policy(&headers, &conn).await?; let data: SendData = data.into_inner().data; - enforce_disable_hide_email_policy(&data, &headers, &mut conn).await?; + enforce_disable_hide_email_policy(&data, &headers, &conn).await?; - let mut send = match Send::find_by_uuid(id, &mut conn).await { + let mut send = match Send::find_by_uuid(id, &conn).await { Some(s) => s, None => err!("Send not found"), }; @@ -571,13 +565,13 @@ async fn put_send( send.set_password(Some(&password)); } - send.save(&mut conn).await?; + send.save(&conn).await?; nt.send_send_update( UpdateType::SyncSendUpdate, &send, - &send.update_users_revision(&mut conn).await, + &send.update_users_revision(&conn).await, &headers.device.uuid, - &mut conn, + &conn, ) .await; @@ -585,8 +579,8 @@ async fn put_send( } #[delete("/sends/")] -async fn delete_send(id: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { - let send = match Send::find_by_uuid(id, &mut conn).await { +async fn delete_send(id: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { + let send = match Send::find_by_uuid(id, &conn).await { Some(s) => s, None => err!("Send not found"), }; @@ -595,13 +589,13 @@ async fn delete_send(id: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_ err!("Send is not owned by user") } - send.delete(&mut conn).await?; + send.delete(&conn).await?; nt.send_send_update( UpdateType::SyncSendDelete, &send, - &send.update_users_revision(&mut conn).await, + &send.update_users_revision(&conn).await, &headers.device.uuid, - &mut conn, + &conn, ) .await; @@ -609,10 +603,10 @@ async fn delete_send(id: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_ } #[put("/sends//remove-password")] -async fn put_remove_password(id: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { - enforce_disable_send_policy(&headers, &mut conn).await?; +async fn put_remove_password(id: &str, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { + enforce_disable_send_policy(&headers, &conn).await?; - let mut send = match Send::find_by_uuid(id, &mut conn).await { + let mut send = match Send::find_by_uuid(id, &conn).await { Some(s) => s, None => err!("Send not found"), }; @@ -622,13 +616,13 @@ async fn put_remove_password(id: &str, headers: Headers, mut conn: DbConn, nt: N } send.set_password(None); - send.save(&mut conn).await?; + send.save(&conn).await?; nt.send_send_update( UpdateType::SyncSendUpdate, &send, - &send.update_users_revision(&mut conn).await, + &send.update_users_revision(&conn).await, &headers.device.uuid, - &mut conn, + &conn, ) .await; diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs index 18740454..252f9be8 100644 --- a/src/api/core/two_factor/authenticator.rs +++ b/src/api/core/two_factor/authenticator.rs @@ -22,7 +22,7 @@ pub fn routes() -> Vec { } #[post("/two-factor/get-authenticator", data = "")] -async fn generate_authenticator(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn generate_authenticator(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: PasswordData = data.into_inner().data; let user = headers.user; @@ -31,7 +31,7 @@ async fn generate_authenticator(data: JsonUpcase, headers: Headers } let type_ = TwoFactorType::Authenticator as i32; - let twofactor = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await; + let twofactor = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await; let (enabled, key) = match twofactor { Some(tf) => (true, tf.data), @@ -57,7 +57,7 @@ struct EnableAuthenticatorData { async fn activate_authenticator( data: JsonUpcase, headers: Headers, - mut conn: DbConn, + conn: DbConn, ) -> JsonResult { let data: EnableAuthenticatorData = data.into_inner().data; let password_hash = data.MasterPasswordHash; @@ -81,11 +81,11 @@ async fn activate_authenticator( } // Validate the token provided with the key, and save new twofactor - validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &headers.ip, &mut conn).await?; + validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &headers.ip, &conn).await?; - _generate_recover_code(&mut user, &mut conn).await; + _generate_recover_code(&mut user, &conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn).await; Ok(Json(json!({ "Enabled": true, @@ -108,7 +108,7 @@ pub async fn validate_totp_code_str( totp_code: &str, secret: &str, ip: &ClientIp, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { if !totp_code.chars().all(char::is_numeric) { err!("TOTP code is not a number"); @@ -122,7 +122,7 @@ pub async fn validate_totp_code( totp_code: &str, secret: &str, ip: &ClientIp, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { use totp_lite::{totp_custom, Sha1}; diff --git a/src/api/core/two_factor/duo.rs b/src/api/core/two_factor/duo.rs index c4ca0ba8..a9f92b85 100644 --- a/src/api/core/two_factor/duo.rs +++ b/src/api/core/two_factor/duo.rs @@ -92,14 +92,14 @@ impl DuoStatus { const DISABLED_MESSAGE_DEFAULT: &str = ""; #[post("/two-factor/get-duo", data = "")] -async fn get_duo(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn get_duo(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: PasswordData = data.into_inner().data; if !headers.user.check_valid_password(&data.MasterPasswordHash) { err!("Invalid password"); } - let data = get_user_duo_data(&headers.user.uuid, &mut conn).await; + let data = get_user_duo_data(&headers.user.uuid, &conn).await; let (enabled, data) = match data { DuoStatus::Global(_) => (true, Some(DuoData::secret())), @@ -155,7 +155,7 @@ fn check_duo_fields_custom(data: &EnableDuoData) -> bool { } #[post("/two-factor/duo", data = "")] -async fn activate_duo(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn activate_duo(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: EnableDuoData = data.into_inner().data; let mut user = headers.user; @@ -174,11 +174,11 @@ async fn activate_duo(data: JsonUpcase, headers: Headers, mut con let type_ = TwoFactorType::Duo; let twofactor = TwoFactor::new(user.uuid.clone(), type_, data_str); - twofactor.save(&mut conn).await?; + twofactor.save(&conn).await?; - _generate_recover_code(&mut user, &mut conn).await; + _generate_recover_code(&mut user, &conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn).await; Ok(Json(json!({ "Enabled": true, @@ -228,7 +228,7 @@ const AUTH_PREFIX: &str = "AUTH"; const DUO_PREFIX: &str = "TX"; const APP_PREFIX: &str = "APP"; -async fn get_user_duo_data(uuid: &str, conn: &mut DbConn) -> DuoStatus { +async fn get_user_duo_data(uuid: &str, conn: &DbConn) -> DuoStatus { let type_ = TwoFactorType::Duo as i32; // If the user doesn't have an entry, disabled @@ -252,7 +252,7 @@ async fn get_user_duo_data(uuid: &str, conn: &mut DbConn) -> DuoStatus { } // let (ik, sk, ak, host) = get_duo_keys(); -async fn get_duo_keys_email(email: &str, conn: &mut DbConn) -> ApiResult<(String, String, String, String)> { +async fn get_duo_keys_email(email: &str, conn: &DbConn) -> ApiResult<(String, String, String, String)> { let data = match User::find_by_mail(email, conn).await { Some(u) => get_user_duo_data(&u.uuid, conn).await.data(), _ => DuoData::global(), @@ -262,7 +262,7 @@ async fn get_duo_keys_email(email: &str, conn: &mut DbConn) -> ApiResult<(String Ok((data.ik, data.sk, CONFIG.get_duo_akey(), data.host)) } -pub async fn generate_duo_signature(email: &str, conn: &mut DbConn) -> ApiResult<(String, String)> { +pub async fn generate_duo_signature(email: &str, conn: &DbConn) -> ApiResult<(String, String)> { let now = Utc::now().timestamp(); let (ik, sk, ak, host) = get_duo_keys_email(email, conn).await?; @@ -280,7 +280,7 @@ fn sign_duo_values(key: &str, email: &str, ikey: &str, prefix: &str, expire: i64 format!("{}|{}", cookie, crypto::hmac_sign(key, &cookie)) } -pub async fn validate_duo_login(email: &str, response: &str, conn: &mut DbConn) -> EmptyResult { +pub async fn validate_duo_login(email: &str, response: &str, conn: &DbConn) -> EmptyResult { // email is as entered by the user, so it needs to be normalized before // comparison with auth_user below. let email = &email.to_lowercase(); diff --git a/src/api/core/two_factor/email.rs b/src/api/core/two_factor/email.rs index 1ca5152b..cdeb79e3 100644 --- a/src/api/core/two_factor/email.rs +++ b/src/api/core/two_factor/email.rs @@ -31,13 +31,13 @@ struct SendEmailLoginData { /// User is trying to login and wants to use email 2FA. /// Does not require Bearer token #[post("/two-factor/send-email-login", data = "")] // JsonResult -async fn send_email_login(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { +async fn send_email_login(data: JsonUpcase, conn: DbConn) -> EmptyResult { let data: SendEmailLoginData = data.into_inner().data; use crate::db::models::User; // Get the user - let user = match User::find_by_mail(&data.Email, &mut conn).await { + let user = match User::find_by_mail(&data.Email, &conn).await { Some(user) => user, None => err!("Username or password is incorrect. Try again."), }; @@ -51,13 +51,13 @@ async fn send_email_login(data: JsonUpcase, mut conn: DbConn err!("Email 2FA is disabled") } - send_token(&user.uuid, &mut conn).await?; + send_token(&user.uuid, &conn).await?; Ok(()) } /// Generate the token, save the data for later verification and send email to user -pub async fn send_token(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { +pub async fn send_token(user_uuid: &str, conn: &DbConn) -> EmptyResult { let type_ = TwoFactorType::Email as i32; let mut twofactor = TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await.map_res("Two factor not found")?; @@ -76,7 +76,7 @@ pub async fn send_token(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { /// When user clicks on Manage email 2FA show the user the related information #[post("/two-factor/get-email", data = "")] -async fn get_email(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn get_email(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: PasswordData = data.into_inner().data; let user = headers.user; @@ -85,7 +85,7 @@ async fn get_email(data: JsonUpcase, headers: Headers, mut conn: D } let (enabled, mfa_email) = - match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email as i32, &mut conn).await { + match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email as i32, &conn).await { Some(x) => { let twofactor_data = EmailTokenData::from_json(&x.data)?; (true, json!(twofactor_data.email)) @@ -110,7 +110,7 @@ struct SendEmailData { /// Send a verification email to the specified email address to check whether it exists/belongs to user. #[post("/two-factor/send-email", data = "")] -async fn send_email(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { +async fn send_email(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { let data: SendEmailData = data.into_inner().data; let user = headers.user; @@ -124,8 +124,8 @@ async fn send_email(data: JsonUpcase, headers: Headers, mut conn: let type_ = TwoFactorType::Email as i32; - if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { - tf.delete(&mut conn).await?; + if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await { + tf.delete(&conn).await?; } let generated_token = crypto::generate_email_token(CONFIG.email_token_size()); @@ -133,7 +133,7 @@ async fn send_email(data: JsonUpcase, headers: Headers, mut conn: // Uses EmailVerificationChallenge as type to show that it's not verified yet. let twofactor = TwoFactor::new(user.uuid, TwoFactorType::EmailVerificationChallenge, twofactor_data.to_json()); - twofactor.save(&mut conn).await?; + twofactor.save(&conn).await?; mail::send_token(&twofactor_data.email, &twofactor_data.last_token.map_res("Token is empty")?).await?; @@ -150,7 +150,7 @@ struct EmailData { /// Verify email belongs to user and can be used for 2FA email codes. #[put("/two-factor/email", data = "")] -async fn email(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn email(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: EmailData = data.into_inner().data; let mut user = headers.user; @@ -160,7 +160,7 @@ async fn email(data: JsonUpcase, headers: Headers, mut conn: DbConn) let type_ = TwoFactorType::EmailVerificationChallenge as i32; let mut twofactor = - TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await.map_res("Two factor not found")?; + TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await.map_res("Two factor not found")?; let mut email_data = EmailTokenData::from_json(&twofactor.data)?; @@ -176,11 +176,11 @@ async fn email(data: JsonUpcase, headers: Headers, mut conn: DbConn) email_data.reset_token(); twofactor.atype = TwoFactorType::Email as i32; twofactor.data = email_data.to_json(); - twofactor.save(&mut conn).await?; + twofactor.save(&conn).await?; - _generate_recover_code(&mut user, &mut conn).await; + _generate_recover_code(&mut user, &conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn).await; Ok(Json(json!({ "Email": email_data.email, @@ -190,7 +190,7 @@ async fn email(data: JsonUpcase, headers: Headers, mut conn: DbConn) } /// Validate the email code when used as TwoFactor token mechanism -pub async fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, conn: &mut DbConn) -> EmptyResult { +pub async fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, conn: &DbConn) -> EmptyResult { let mut email_data = EmailTokenData::from_json(data)?; let mut twofactor = TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Email as i32, conn) .await diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs index 35c1867f..ebfa10c6 100644 --- a/src/api/core/two_factor/mod.rs +++ b/src/api/core/two_factor/mod.rs @@ -38,8 +38,8 @@ pub fn routes() -> Vec { } #[get("/two-factor")] -async fn get_twofactor(headers: Headers, mut conn: DbConn) -> Json { - let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &mut conn).await; +async fn get_twofactor(headers: Headers, conn: DbConn) -> Json { + let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn).await; let twofactors_json: Vec = twofactors.iter().map(TwoFactor::to_json_provider).collect(); Json(json!({ @@ -73,13 +73,13 @@ struct RecoverTwoFactor { } #[post("/two-factor/recover", data = "")] -async fn recover(data: JsonUpcase, client_headers: ClientHeaders, mut conn: DbConn) -> JsonResult { +async fn recover(data: JsonUpcase, client_headers: ClientHeaders, conn: DbConn) -> JsonResult { let data: RecoverTwoFactor = data.into_inner().data; use crate::db::models::User; // Get the user - let mut user = match User::find_by_mail(&data.Email, &mut conn).await { + let mut user = match User::find_by_mail(&data.Email, &conn).await { Some(user) => user, None => err!("Username or password is incorrect. Try again."), }; @@ -95,24 +95,24 @@ async fn recover(data: JsonUpcase, client_headers: ClientHeade } // Remove all twofactors from the user - TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?; + TwoFactor::delete_all_by_user(&user.uuid, &conn).await?; log_user_event( EventType::UserRecovered2fa as i32, &user.uuid, client_headers.device_type, &client_headers.ip.ip, - &mut conn, + &conn, ) .await; // Remove the recovery code, not needed without twofactors user.totp_recover = None; - user.save(&mut conn).await?; + user.save(&conn).await?; Ok(Json(Value::Object(serde_json::Map::new()))) } -async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) { +async fn _generate_recover_code(user: &mut User, conn: &DbConn) { if user.totp_recover.is_none() { let totp_recover = crypto::encode_random_bytes::<20>(BASE32); user.totp_recover = Some(totp_recover); @@ -128,7 +128,7 @@ struct DisableTwoFactorData { } #[post("/two-factor/disable", data = "")] -async fn disable_twofactor(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn disable_twofactor(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: DisableTwoFactorData = data.into_inner().data; let password_hash = data.MasterPasswordHash; let user = headers.user; @@ -139,26 +139,26 @@ async fn disable_twofactor(data: JsonUpcase, headers: Head let type_ = data.Type.into_i32()?; - if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { - twofactor.delete(&mut conn).await?; - log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn) + if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await { + twofactor.delete(&conn).await?; + log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn) .await; } - let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty(); + let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &conn).await.is_empty(); if twofactor_disabled { for user_org in - UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, &mut conn) + UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, &conn) .await .into_iter() { if user_org.atype < UserOrgType::Admin { if CONFIG.mail_enabled() { - let org = Organization::find_by_uuid(&user_org.org_uuid, &mut conn).await.unwrap(); + let org = Organization::find_by_uuid(&user_org.org_uuid, &conn).await.unwrap(); mail::send_2fa_removed_from_org(&user.email, &org.name).await?; } - user_org.delete(&mut conn).await?; + user_org.delete(&conn).await?; } } } @@ -182,7 +182,7 @@ pub async fn send_incomplete_2fa_notifications(pool: DbPool) { return; } - let mut conn = match pool.get().await { + let conn = match pool.get().await { Ok(conn) => conn, _ => { error!("Failed to get DB connection in send_incomplete_2fa_notifications()"); @@ -193,9 +193,9 @@ pub async fn send_incomplete_2fa_notifications(pool: DbPool) { let now = Utc::now().naive_utc(); let time_limit = Duration::minutes(CONFIG.incomplete_2fa_time_limit()); let time_before = now - time_limit; - let incomplete_logins = TwoFactorIncomplete::find_logins_before(&time_before, &mut conn).await; + let incomplete_logins = TwoFactorIncomplete::find_logins_before(&time_before, &conn).await; for login in incomplete_logins { - let user = User::find_by_uuid(&login.user_uuid, &mut conn).await.expect("User not found"); + let user = User::find_by_uuid(&login.user_uuid, &conn).await.expect("User not found"); info!( "User {} did not complete a 2FA login within the configured time limit. IP: {}", user.email, login.ip_address @@ -203,7 +203,7 @@ pub async fn send_incomplete_2fa_notifications(pool: DbPool) { mail::send_incomplete_2fa_login(&user.email, &login.ip_address, &login.login_time, &login.device_name) .await .expect("Error sending incomplete 2FA email"); - login.delete(&mut conn).await.expect("Error deleting incomplete 2FA record"); + login.delete(&conn).await.expect("Error deleting incomplete 2FA record"); } } diff --git a/src/api/core/two_factor/webauthn.rs b/src/api/core/two_factor/webauthn.rs index 3c62754a..2cde8123 100644 --- a/src/api/core/two_factor/webauthn.rs +++ b/src/api/core/two_factor/webauthn.rs @@ -103,7 +103,7 @@ impl WebauthnRegistration { } #[post("/two-factor/get-webauthn", data = "")] -async fn get_webauthn(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn get_webauthn(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { if !CONFIG.domain_set() { err!("`DOMAIN` environment variable is not set. Webauthn disabled") } @@ -112,7 +112,7 @@ async fn get_webauthn(data: JsonUpcase, headers: Headers, mut conn err!("Invalid password"); } - let (enabled, registrations) = get_webauthn_registrations(&headers.user.uuid, &mut conn).await?; + let (enabled, registrations) = get_webauthn_registrations(&headers.user.uuid, &conn).await?; let registrations_json: Vec = registrations.iter().map(WebauthnRegistration::to_json).collect(); Ok(Json(json!({ @@ -123,12 +123,12 @@ async fn get_webauthn(data: JsonUpcase, headers: Headers, mut conn } #[post("/two-factor/get-webauthn-challenge", data = "")] -async fn generate_webauthn_challenge(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn generate_webauthn_challenge(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { if !headers.user.check_valid_password(&data.data.MasterPasswordHash) { err!("Invalid password"); } - let registrations = get_webauthn_registrations(&headers.user.uuid, &mut conn) + let registrations = get_webauthn_registrations(&headers.user.uuid, &conn) .await? .1 .into_iter() @@ -145,7 +145,7 @@ async fn generate_webauthn_challenge(data: JsonUpcase, headers: He )?; let type_ = TwoFactorType::WebauthnRegisterChallenge; - TwoFactor::new(headers.user.uuid, type_, serde_json::to_string(&state)?).save(&mut conn).await?; + TwoFactor::new(headers.user.uuid, type_, serde_json::to_string(&state)?).save(&conn).await?; let mut challenge_value = serde_json::to_value(challenge.public_key)?; challenge_value["status"] = "ok".into(); @@ -242,7 +242,7 @@ impl From for PublicKeyCredential { } #[post("/two-factor/webauthn", data = "")] -async fn activate_webauthn(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn activate_webauthn(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: EnableWebauthnData = data.into_inner().data; let mut user = headers.user; @@ -252,10 +252,10 @@ async fn activate_webauthn(data: JsonUpcase, headers: Header // Retrieve and delete the saved challenge state let type_ = TwoFactorType::WebauthnRegisterChallenge as i32; - let state = match TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { + let state = match TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await { Some(tf) => { let state: RegistrationState = serde_json::from_str(&tf.data)?; - tf.delete(&mut conn).await?; + tf.delete(&conn).await?; state } None => err!("Can't recover challenge"), @@ -265,7 +265,7 @@ async fn activate_webauthn(data: JsonUpcase, headers: Header let (credential, _data) = WebauthnConfig::load().register_credential(&data.DeviceResponse.into(), &state, |_| Ok(false))?; - let mut registrations: Vec<_> = get_webauthn_registrations(&user.uuid, &mut conn).await?.1; + let mut registrations: Vec<_> = get_webauthn_registrations(&user.uuid, &conn).await?.1; // TODO: Check for repeated ID's registrations.push(WebauthnRegistration { id: data.Id.into_i32()?, @@ -277,11 +277,11 @@ async fn activate_webauthn(data: JsonUpcase, headers: Header // Save the registrations and return them TwoFactor::new(user.uuid.clone(), TwoFactorType::Webauthn, serde_json::to_string(®istrations)?) - .save(&mut conn) + .save(&conn) .await?; - _generate_recover_code(&mut user, &mut conn).await; + _generate_recover_code(&mut user, &conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn).await; let keys_json: Vec = registrations.iter().map(WebauthnRegistration::to_json).collect(); Ok(Json(json!({ @@ -304,17 +304,17 @@ struct DeleteU2FData { } #[delete("/two-factor/webauthn", data = "")] -async fn delete_webauthn(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn delete_webauthn(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let id = data.data.Id.into_i32()?; if !headers.user.check_valid_password(&data.data.MasterPasswordHash) { err!("Invalid password"); } - let mut tf = - match TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::Webauthn as i32, &mut conn).await { - Some(tf) => tf, - None => err!("Webauthn data not found!"), - }; + let mut tf = match TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::Webauthn as i32, &conn).await + { + Some(tf) => tf, + None => err!("Webauthn data not found!"), + }; let mut data: Vec = serde_json::from_str(&tf.data)?; @@ -325,12 +325,11 @@ async fn delete_webauthn(data: JsonUpcase, headers: Headers, mut let removed_item = data.remove(item_pos); tf.data = serde_json::to_string(&data)?; - tf.save(&mut conn).await?; + tf.save(&conn).await?; drop(tf); // If entry is migrated from u2f, delete the u2f entry as well - if let Some(mut u2f) = - TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::U2f as i32, &mut conn).await + if let Some(mut u2f) = TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::U2f as i32, &conn).await { let mut data: Vec = match serde_json::from_str(&u2f.data) { Ok(d) => d, @@ -341,7 +340,7 @@ async fn delete_webauthn(data: JsonUpcase, headers: Headers, mut let new_data_str = serde_json::to_string(&data)?; u2f.data = new_data_str; - u2f.save(&mut conn).await?; + u2f.save(&conn).await?; } let keys_json: Vec = data.iter().map(WebauthnRegistration::to_json).collect(); @@ -355,7 +354,7 @@ async fn delete_webauthn(data: JsonUpcase, headers: Headers, mut pub async fn get_webauthn_registrations( user_uuid: &str, - conn: &mut DbConn, + conn: &DbConn, ) -> Result<(bool, Vec), Error> { let type_ = TwoFactorType::Webauthn as i32; match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await { @@ -364,7 +363,7 @@ pub async fn get_webauthn_registrations( } } -pub async fn generate_webauthn_login(user_uuid: &str, conn: &mut DbConn) -> JsonResult { +pub async fn generate_webauthn_login(user_uuid: &str, conn: &DbConn) -> JsonResult { // Load saved credentials let creds: Vec = get_webauthn_registrations(user_uuid, conn).await?.1.into_iter().map(|r| r.credential).collect(); @@ -386,7 +385,7 @@ pub async fn generate_webauthn_login(user_uuid: &str, conn: &mut DbConn) -> Json Ok(Json(serde_json::to_value(response.public_key)?)) } -pub async fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &mut DbConn) -> EmptyResult { +pub async fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult { let type_ = TwoFactorType::WebauthnLoginChallenge as i32; let state = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await { Some(tf) => { diff --git a/src/api/core/two_factor/yubikey.rs b/src/api/core/two_factor/yubikey.rs index 7681ab01..2e3629bb 100644 --- a/src/api/core/two_factor/yubikey.rs +++ b/src/api/core/two_factor/yubikey.rs @@ -83,7 +83,7 @@ async fn verify_yubikey_otp(otp: String) -> EmptyResult { } #[post("/two-factor/get-yubikey", data = "")] -async fn generate_yubikey(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn generate_yubikey(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { // Make sure the credentials are set get_yubico_credentials()?; @@ -97,7 +97,7 @@ async fn generate_yubikey(data: JsonUpcase, headers: Headers, mut let user_uuid = &user.uuid; let yubikey_type = TwoFactorType::YubiKey as i32; - let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &mut conn).await; + let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn).await; if let Some(r) = r { let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?; @@ -118,7 +118,7 @@ async fn generate_yubikey(data: JsonUpcase, headers: Headers, mut } #[post("/two-factor/yubikey", data = "")] -async fn activate_yubikey(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { +async fn activate_yubikey(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { let data: EnableYubikeyData = data.into_inner().data; let mut user = headers.user; @@ -128,7 +128,7 @@ async fn activate_yubikey(data: JsonUpcase, headers: Headers, // Check if we already have some data let mut yubikey_data = - match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey as i32, &mut conn).await { + match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey as i32, &conn).await { Some(data) => data, None => TwoFactor::new(user.uuid.clone(), TwoFactorType::YubiKey, String::new()), }; @@ -160,11 +160,11 @@ async fn activate_yubikey(data: JsonUpcase, headers: Headers, }; yubikey_data.data = serde_json::to_string(&yubikey_metadata).unwrap(); - yubikey_data.save(&mut conn).await?; + yubikey_data.save(&conn).await?; - _generate_recover_code(&mut user, &mut conn).await; + _generate_recover_code(&mut user, &conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &conn).await; let mut result = jsonify_yubikeys(yubikey_metadata.Keys); diff --git a/src/api/identity.rs b/src/api/identity.rs index e5611610..41d162f4 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -25,7 +25,7 @@ pub fn routes() -> Vec { } #[post("/connect/token", data = "")] -async fn login(data: Form, client_header: ClientHeaders, mut conn: DbConn) -> JsonResult { +async fn login(data: Form, client_header: ClientHeaders, conn: DbConn) -> JsonResult { let data: ConnectData = data.into_inner(); let mut user_uuid: Option = None; @@ -33,7 +33,7 @@ async fn login(data: Form, client_header: ClientHeaders, mut conn: let login_result = match data.grant_type.as_ref() { "refresh_token" => { _check_is_some(&data.refresh_token, "refresh_token cannot be blank")?; - _refresh_login(data, &mut conn).await + _refresh_login(data, &conn).await } "password" => { _check_is_some(&data.client_id, "client_id cannot be blank")?; @@ -45,7 +45,7 @@ async fn login(data: Form, client_header: ClientHeaders, mut conn: _check_is_some(&data.device_name, "device_name cannot be blank")?; _check_is_some(&data.device_type, "device_type cannot be blank")?; - _password_login(data, &mut user_uuid, &mut conn, &client_header.ip).await + _password_login(data, &mut user_uuid, &conn, &client_header.ip).await } "client_credentials" => { _check_is_some(&data.client_id, "client_id cannot be blank")?; @@ -56,7 +56,7 @@ async fn login(data: Form, client_header: ClientHeaders, mut conn: _check_is_some(&data.device_name, "device_name cannot be blank")?; _check_is_some(&data.device_type, "device_type cannot be blank")?; - _api_key_login(data, &mut user_uuid, &mut conn, &client_header.ip).await + _api_key_login(data, &mut user_uuid, &conn, &client_header.ip).await } t => err!("Invalid type", t), }; @@ -69,20 +69,14 @@ async fn login(data: Form, client_header: ClientHeaders, mut conn: &user_uuid, client_header.device_type, &client_header.ip.ip, - &mut conn, + &conn, ) .await; } Err(e) => { if let Some(ev) = e.get_event() { - log_user_event( - ev.event as i32, - &user_uuid, - client_header.device_type, - &client_header.ip.ip, - &mut conn, - ) - .await + log_user_event(ev.event as i32, &user_uuid, client_header.device_type, &client_header.ip.ip, &conn) + .await } } } @@ -91,7 +85,7 @@ async fn login(data: Form, client_header: ClientHeaders, mut conn: login_result } -async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult { +async fn _refresh_login(data: ConnectData, conn: &DbConn) -> JsonResult { // Extract token let token = data.refresh_token.unwrap(); @@ -130,7 +124,7 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult { async fn _password_login( data: ConnectData, user_uuid: &mut Option, - conn: &mut DbConn, + conn: &DbConn, ip: &ClientIp, ) -> JsonResult { // Validate scope @@ -294,12 +288,7 @@ async fn _password_login( Ok(Json(result)) } -async fn _api_key_login( - data: ConnectData, - user_uuid: &mut Option, - conn: &mut DbConn, - ip: &ClientIp, -) -> JsonResult { +async fn _api_key_login(data: ConnectData, user_uuid: &mut Option, conn: &DbConn, ip: &ClientIp) -> JsonResult { // Ratelimit the login crate::ratelimit::check_limit_login(&ip.ip)?; @@ -314,7 +303,7 @@ async fn _api_key_login( async fn _user_api_key_login( data: ConnectData, user_uuid: &mut Option, - conn: &mut DbConn, + conn: &DbConn, ip: &ClientIp, ) -> JsonResult { // Get the user via the client_id @@ -401,7 +390,7 @@ async fn _user_api_key_login( Ok(Json(result)) } -async fn _organization_api_key_login(data: ConnectData, conn: &mut DbConn, ip: &ClientIp) -> JsonResult { +async fn _organization_api_key_login(data: ConnectData, conn: &DbConn, ip: &ClientIp) -> JsonResult { // Get the org via the client_id let client_id = data.client_id.as_ref().unwrap(); let org_uuid = match client_id.strip_prefix("organization.") { @@ -432,7 +421,7 @@ async fn _organization_api_key_login(data: ConnectData, conn: &mut DbConn, ip: & } /// Retrieves an existing device or creates a new device from ConnectData and the User -async fn get_device(data: &ConnectData, conn: &mut DbConn, user: &User) -> (Device, bool) { +async fn get_device(data: &ConnectData, conn: &DbConn, user: &User) -> (Device, bool) { // On iOS, device_type sends "iOS", on others it sends a number // When unknown or unable to parse, return 14, which is 'Unknown Browser' let device_type = util::try_parse_string(data.device_type.as_ref()).unwrap_or(14); @@ -457,7 +446,7 @@ async fn twofactor_auth( data: &ConnectData, device: &mut Device, ip: &ClientIp, - conn: &mut DbConn, + conn: &DbConn, ) -> ApiResult> { let twofactors = TwoFactor::find_by_user(user_uuid, conn).await; @@ -534,7 +523,7 @@ fn _selected_data(tf: Option) -> ApiResult { tf.map(|t| t.data).map_res("Two factor doesn't exist") } -async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbConn) -> ApiResult { +async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> ApiResult { use crate::api::core::two_factor; let mut result = json!({ diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 5bcc59b5..ccab4c64 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -371,7 +371,7 @@ impl WebSocketUsers { ut: UpdateType, folder: &Folder, acting_device_uuid: &String, - conn: &mut DbConn, + conn: &DbConn, ) { let data = create_update( vec![ @@ -397,7 +397,7 @@ impl WebSocketUsers { user_uuids: &[String], acting_device_uuid: &String, collection_uuids: Option>, - conn: &mut DbConn, + conn: &DbConn, ) { let org_uuid = convert_option(cipher.organization_uuid.clone()); // Depending if there are collections provided or not, we need to have different values for the following variables. @@ -439,7 +439,7 @@ impl WebSocketUsers { send: &DbSend, user_uuids: &[String], acting_device_uuid: &String, - conn: &mut DbConn, + conn: &DbConn, ) { let user_uuid = convert_option(send.user_uuid.clone()); @@ -466,7 +466,7 @@ impl WebSocketUsers { user_uuid: &String, auth_request_uuid: &String, acting_device_uuid: &String, - conn: &mut DbConn, + conn: &DbConn, ) { let data = create_update( vec![("Id".into(), auth_request_uuid.clone().into()), ("UserId".into(), user_uuid.clone().into())], @@ -485,7 +485,7 @@ impl WebSocketUsers { user_uuid: &String, auth_response_uuid: &str, approving_device_uuid: String, - conn: &mut DbConn, + conn: &DbConn, ) { let data = create_update( vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.clone().into())], diff --git a/src/api/push.rs b/src/api/push.rs index 3b0a573b..cd307094 100644 --- a/src/api/push.rs +++ b/src/api/push.rs @@ -4,7 +4,10 @@ use tokio::sync::RwLock; use crate::{ api::{ApiResult, EmptyResult, UpdateType}, - db::models::{Cipher, Device, Folder, Send, User}, + db::{ + models::{Cipher, Device, Folder, Send, User}, + DbConn, + }, util::get_reqwest_client, CONFIG, }; @@ -121,12 +124,7 @@ pub async fn unregister_push_device(uuid: String) -> EmptyResult { Ok(()) } -pub async fn push_cipher_update( - ut: UpdateType, - cipher: &Cipher, - acting_device_uuid: &String, - conn: &mut crate::db::DbConn, -) { +pub async fn push_cipher_update(ut: UpdateType, cipher: &Cipher, acting_device_uuid: &String, conn: &DbConn) { // We shouldn't send a push notification on cipher update if the cipher belongs to an organization, this isn't implemented in the upstream server too. if cipher.organization_uuid.is_some() { return; @@ -187,12 +185,7 @@ pub fn push_user_update(ut: UpdateType, user: &User) { }))); } -pub async fn push_folder_update( - ut: UpdateType, - folder: &Folder, - acting_device_uuid: &String, - conn: &mut crate::db::DbConn, -) { +pub async fn push_folder_update(ut: UpdateType, folder: &Folder, acting_device_uuid: &String, conn: &DbConn) { if Device::check_user_has_push_device(&folder.user_uuid, conn).await { tokio::task::spawn(send_to_push_relay(json!({ "userId": folder.user_uuid, @@ -209,7 +202,7 @@ pub async fn push_folder_update( } } -pub async fn push_send_update(ut: UpdateType, send: &Send, acting_device_uuid: &String, conn: &mut crate::db::DbConn) { +pub async fn push_send_update(ut: UpdateType, send: &Send, acting_device_uuid: &String, conn: &DbConn) { if let Some(s) = &send.user_uuid { if Device::check_user_has_push_device(s, conn).await { tokio::task::spawn(send_to_push_relay(json!({ @@ -256,7 +249,7 @@ async fn send_to_push_relay(notification_data: Value) { }; } -pub async fn push_auth_request(user_uuid: String, auth_request_uuid: String, conn: &mut crate::db::DbConn) { +pub async fn push_auth_request(user_uuid: String, auth_request_uuid: String, conn: &DbConn) { if Device::check_user_has_push_device(user_uuid.as_str(), conn).await { tokio::task::spawn(send_to_push_relay(json!({ "userId": user_uuid, @@ -276,7 +269,7 @@ pub async fn push_auth_response( user_uuid: String, auth_request_uuid: String, approving_device_uuid: String, - conn: &mut crate::db::DbConn, + conn: &DbConn, ) { if Device::check_user_has_push_device(user_uuid.as_str(), conn).await { tokio::task::spawn(send_to_push_relay(json!({ diff --git a/src/auth.rs b/src/auth.rs index e23aa32d..5d008b89 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -442,17 +442,17 @@ impl<'r> FromRequest<'r> for Headers { let device_uuid = claims.device; let user_uuid = claims.sub; - let mut conn = match DbConn::from_request(request).await { + let conn = match DbConn::from_request(request).await { Outcome::Success(conn) => conn, _ => err_handler!("Error getting DB"), }; - let device = match Device::find_by_uuid_and_user(&device_uuid, &user_uuid, &mut conn).await { + let device = match Device::find_by_uuid_and_user(&device_uuid, &user_uuid, &conn).await { Some(device) => device, None => err_handler!("Invalid device id"), }; - let user = match User::find_by_uuid(&user_uuid, &mut conn).await { + let user = match User::find_by_uuid(&user_uuid, &conn).await { Some(user) => user, None => err_handler!("Device has no user associated"), }; @@ -474,7 +474,7 @@ impl<'r> FromRequest<'r> for Headers { // This prevents checking this stamp exception for new requests. let mut user = user; user.reset_stamp_exception(); - if let Err(e) = user.save(&mut conn).await { + if let Err(e) = user.save(&conn).await { error!("Error updating user: {:#?}", e); } err_handler!("Stamp exception is expired") @@ -536,13 +536,13 @@ impl<'r> FromRequest<'r> for OrgHeaders { match url_org_id { Some(org_id) => { - let mut conn = match DbConn::from_request(request).await { + let conn = match DbConn::from_request(request).await { Outcome::Success(conn) => conn, _ => err_handler!("Error getting DB"), }; let user = headers.user; - let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await { + let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, org_id, &conn).await { Some(user) => { if user.status == UserOrgStatus::Confirmed as i32 { user @@ -656,12 +656,12 @@ impl<'r> FromRequest<'r> for ManagerHeaders { if headers.org_user_type >= UserOrgType::Manager { match get_col_id(request) { Some(col_id) => { - let mut conn = match DbConn::from_request(request).await { + let conn = match DbConn::from_request(request).await { Outcome::Success(conn) => conn, _ => err_handler!("Error getting DB"), }; - if !can_access_collection(&headers.org_user, &col_id, &mut conn).await { + if !can_access_collection(&headers.org_user, &col_id, &conn).await { err_handler!("The current user isn't a manager for this collection") } } @@ -734,7 +734,7 @@ impl From for Headers { } } } -async fn can_access_collection(org_user: &UserOrganization, col_id: &str, conn: &mut DbConn) -> bool { +async fn can_access_collection(org_user: &UserOrganization, col_id: &str, conn: &DbConn) -> bool { org_user.has_full_access() || Collection::has_access_by_collection_and_user_uuid(col_id, &org_user.user_uuid, conn).await } @@ -743,7 +743,7 @@ impl ManagerHeaders { pub async fn from_loose( h: ManagerHeadersLoose, collections: &Vec, - conn: &mut DbConn, + conn: &DbConn, ) -> Result { for col_id in collections { if uuid::Uuid::parse_str(col_id).is_err() { diff --git a/src/config.rs b/src/config.rs index 67ba66ae..1259b78a 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}, }; @@ -689,12 +688,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 9f3582d7..91ac17a3 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, )+ } +#[derive(diesel::MultiConnection)] +pub enum DbConnInner { + #[cfg(sqlite)] + Sqlite(diesel::sqlite::SqliteConnection), + #[cfg(mysql)] + Mysql(diesel::mysql::MysqlConnection), + #[cfg(postgresql)] + Postgresql(diesel::pg::PgConnection), +} - pub struct DbConn { - conn: Arc>>, - permit: Option, +#[derive(Eq, PartialEq)] +pub enum DbConnType { + #[cfg(sqlite)] + Sqlite, + #[cfg(mysql)] + Mysql, + #[cfg(postgresql)] + Postgresql, +} + +pub struct DbConn { + conn: Arc>>>>, + permit: Option, +} + +#[derive(Debug)] +pub struct DbConnOptions { + pub init_stmts: String, +} + +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)] - pub enum DbConnInner { $( #[cfg($name)] $name(PooledConnection>), )+ } +#[derive(Clone)] +pub struct DbPool { + pool: Option>>, + semaphore: Arc, +} - #[derive(Debug)] - pub struct DbConnOptions { - pub init_stmts: String, +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(); + // 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 + } +} - $( // 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)?; +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)?; } - Ok(()) } - })+ - - #[derive(Clone)] - pub struct DbPool { - // This is an 'Option' so that we can drop the pool in a 'spawn_blocking'. - pool: Option, - semaphore: Arc - } - - #[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); - } - - // Drop permit after the connection is dropped - drop(permit); - }); + #[cfg(mysql)] + DbConnType::Mysql => { + #[cfg(feature = "mysql")] + { + mysql_migrations::run_migrations(&url)?; + } + } + #[cfg(postgresql)] + DbConnType::Postgresql => { + #[cfg(feature = "postgresql")] + { + postgresql_migrations::run_migrations(&url)?; + } } } - impl Drop for DbPool { - fn drop(&mut self) { - let pool = self.pool.take(); - tokio::task::spawn_blocking(move || drop(pool)); + 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)), + }) + } + + 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"); } - } + }; - 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") - }, - )+ } - } - // 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(not(query_logger))] -generate_connections! { - sqlite: diesel::sqlite::SqliteConnection, - mysql: diesel::mysql::MysqlConnection, - postgresql: diesel::pg::PgConnection -} - -#[cfg(query_logger)] -generate_connections! { - sqlite: diesel_logger::LoggingConnection, - mysql: diesel_logger::LoggingConnection, - postgresql: diesel_logger::LoggingConnection + 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(), } } } +// 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; + let $conn = conn.as_mut().expect("internal invariant broken: self.conn is Some"); + }; +} + #[macro_export] macro_rules! db_run { - // Same for all dbs - ( $conn:ident: $body:block ) => { - db_run! { $conn: sqlite, mysql, postgresql $body } - }; + ( $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 + }}; - ( @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; - - 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 - }, - )+)+ - } + 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 + }, + )+)+} }}; } -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) - } -} - -// 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(), )+ } } - } - - 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, )+ } } - } - } - }; -} +#[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. -pub async fn backup_database(conn: &mut DbConn) -> Result<(), Error> { - db_run! {@raw conn: +#[allow(unused_variables)] // Since we do not use `conn` in PostgreSQL and MySQL +pub async fn backup_database(conn: &DbConn) -> Result<(), Error> { + db_run! {conn: postgresql, mysql { - let _ = conn; err!("PostgreSQL and MySQL/MariaDB do not support this backup feature"); } sqlite { @@ -387,8 +282,8 @@ 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: +pub async fn get_sql_server_version(conn: &DbConn) -> String { + 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; // 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)?; connection.run_pending_migrations(MIGRATIONS).expect("Error running migrations"); Ok(()) } diff --git a/src/db/models/attachment.rs b/src/db/models/attachment.rs index 616aae2f..86e53ca0 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 @@ -60,11 +59,11 @@ use crate::error::MapResult; /// Database methods impl Attachment { - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &DbConn) -> EmptyResult { 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,19 +79,18 @@ 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") } } } - pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { db_run! { conn: { crate::util::retry( || diesel::delete(attachments::table.filter(attachments::id.eq(&self.id))).execute(conn), @@ -116,34 +114,32 @@ impl Attachment { }} } - pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult { for attachment in Attachment::find_by_cipher(cipher_uuid, conn).await { attachment.delete(conn).await?; } Ok(()) } - pub async fn find_by_id(id: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_id(id: &str, conn: &DbConn) -> Option { db_run! { conn: { attachments::table .filter(attachments::id.eq(id.to_lowercase())) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_cipher(cipher_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { attachments::table .filter(attachments::cipher_uuid.eq(cipher_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading attachments") - .from_db() }} } - pub async fn size_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn size_by_user(user_uuid: &str, conn: &DbConn) -> i64 { db_run! { conn: { let result: Option = attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) @@ -155,7 +151,7 @@ impl Attachment { }} } - pub async fn count_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn count_by_user(user_uuid: &str, conn: &DbConn) -> i64 { db_run! { conn: { attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) @@ -166,7 +162,7 @@ impl Attachment { }} } - pub async fn size_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn size_by_org(org_uuid: &str, conn: &DbConn) -> i64 { db_run! { conn: { let result: Option = attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) @@ -178,7 +174,7 @@ impl Attachment { }} } - pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 { db_run! { conn: { attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) @@ -192,16 +188,15 @@ impl Attachment { // This will return all attachments linked to the user or org // There is no filtering done here if the user actually has access! // It is used to speed up the sync process, and the matching is done in a different part. - pub async fn find_all_by_user_and_orgs(user_uuid: &str, org_uuids: &Vec, conn: &mut DbConn) -> Vec { + pub async fn find_all_by_user_and_orgs(user_uuid: &str, org_uuids: &Vec, conn: &DbConn) -> Vec { db_run! { conn: { attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) .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 2a004fb1..4f3f1693 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: Option, + pub enc_key: Option, - pub master_password_hash: Option, - pub approved: Option, - pub creation_date: NaiveDateTime, - pub response_date: Option, + pub master_password_hash: Option, + pub approved: Option, + pub creation_date: NaiveDateTime, + pub response_date: Option, - pub authentication_date: Option, - } + pub authentication_date: Option, } impl AuthRequest { @@ -69,11 +68,11 @@ use crate::api::EmptyResult; use crate::error::MapResult; impl AuthRequest { - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { 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,45 +88,43 @@ 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") } } } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! {conn: { auth_requests::table .filter(auth_requests::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { 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") }} } - pub async fn find_created_before(dt: &NaiveDateTime, conn: &mut DbConn) -> Vec { + pub async fn find_created_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec { 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") }} } - pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(auth_requests::table.filter(auth_requests::uuid.eq(&self.uuid))) .execute(conn) @@ -139,7 +136,7 @@ impl AuthRequest { ct_eq(&self.access_code, access_code) } - pub async fn purge_expired_auth_requests(conn: &mut DbConn) { + pub async fn purge_expired_auth_requests(conn: &DbConn) { let expiry_time = Utc::now().naive_utc() - chrono::Duration::minutes(5); //after 5 minutes, clients reject the request for auth_request in Self::find_created_before(&expiry_time, conn).await { auth_request.delete(conn).await.ok(); diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index f76490b4..f0db5dca 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, +#[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, + 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, + /* + 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 data: String, - pub password_history: Option, - pub deleted_at: Option, - pub reprompt: Option, - } + pub password_history: Option, + pub deleted_at: Option, + pub reprompt: Option, } #[allow(dead_code)] @@ -116,7 +113,7 @@ impl Cipher { user_uuid: &str, cipher_sync_data: Option<&CipherSyncData>, sync_type: CipherSyncType, - conn: &mut DbConn, + conn: &DbConn, ) -> Value { use crate::util::format_date; @@ -261,7 +258,7 @@ impl Cipher { json_object } - pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec { + pub async fn update_users_revision(&self, conn: &DbConn) -> Vec { let mut user_uuids = Vec::new(); match self.user_uuid { Some(ref user_uuid) => { @@ -281,14 +278,14 @@ impl Cipher { user_uuids } - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { self.update_users_revision(conn).await; self.updated_at = Utc::now().naive_utc(); 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,19 +301,18 @@ 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") } } } - pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { self.update_users_revision(conn).await; FolderCipher::delete_all_by_cipher(&self.uuid, conn).await?; @@ -331,7 +327,7 @@ impl Cipher { }} } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { // TODO: Optimize this by executing a DELETE directly on the database, instead of first fetching. for cipher in Self::find_by_org(org_uuid, conn).await { cipher.delete(conn).await?; @@ -339,7 +335,7 @@ impl Cipher { Ok(()) } - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { for cipher in Self::find_owned_by_user(user_uuid, conn).await { cipher.delete(conn).await?; } @@ -347,7 +343,7 @@ impl Cipher { } /// Purge all ciphers that are old enough to be auto-deleted. - pub async fn purge_trash(conn: &mut DbConn) { + pub async fn purge_trash(conn: &DbConn) { if let Some(auto_delete_days) = CONFIG.trash_auto_delete_days() { let now = Utc::now().naive_utc(); let dt = now - Duration::days(auto_delete_days); @@ -357,7 +353,7 @@ impl Cipher { } } - pub async fn move_to_folder(&self, folder_uuid: Option, user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn move_to_folder(&self, folder_uuid: Option, user_uuid: &str, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(user_uuid, conn).await; match (self.get_folder_uuid(user_uuid, conn).await, folder_uuid) { @@ -394,7 +390,7 @@ impl Cipher { &self, user_uuid: &str, cipher_sync_data: Option<&CipherSyncData>, - conn: &mut DbConn, + conn: &DbConn, ) -> bool { if let Some(ref org_uuid) = self.organization_uuid { if let Some(cipher_sync_data) = cipher_sync_data { @@ -413,7 +409,7 @@ impl Cipher { &self, user_uuid: &str, cipher_sync_data: Option<&CipherSyncData>, - conn: &mut DbConn, + conn: &DbConn, ) -> bool { if let Some(ref org_uuid) = self.organization_uuid { if let Some(cipher_sync_data) = cipher_sync_data { @@ -434,7 +430,7 @@ impl Cipher { &self, user_uuid: &str, cipher_sync_data: Option<&CipherSyncData>, - conn: &mut DbConn, + conn: &DbConn, ) -> Option<(bool, bool)> { // Check whether this cipher is directly owned by the user, or is in // a collection that the user has full access to. If so, there are no @@ -492,7 +488,7 @@ impl Cipher { Some((read_only, hide_passwords)) } - async fn get_user_collections_access_flags(&self, user_uuid: &str, conn: &mut DbConn) -> Vec<(bool, bool)> { + async fn get_user_collections_access_flags(&self, user_uuid: &str, conn: &DbConn) -> Vec<(bool, bool)> { db_run! {conn: { // Check whether this cipher is in any collections accessible to the // user. If so, retrieve the access flags for each collection. @@ -509,7 +505,7 @@ impl Cipher { }} } - async fn get_group_collections_access_flags(&self, user_uuid: &str, conn: &mut DbConn) -> Vec<(bool, bool)> { + async fn get_group_collections_access_flags(&self, user_uuid: &str, conn: &DbConn) -> Vec<(bool, bool)> { db_run! {conn: { ciphers::table .filter(ciphers::uuid.eq(&self.uuid)) @@ -532,31 +528,31 @@ impl Cipher { }} } - pub async fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool { match self.get_access_restrictions(user_uuid, None, conn).await { Some((read_only, _hide_passwords)) => !read_only, None => false, } } - pub async fn is_accessible_to_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn is_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool { self.get_access_restrictions(user_uuid, None, conn).await.is_some() } // Returns whether this cipher is a favorite of the specified user. - pub async fn is_favorite(&self, user_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn is_favorite(&self, user_uuid: &str, conn: &DbConn) -> bool { Favorite::is_favorite(&self.uuid, user_uuid, conn).await } // Sets whether this cipher is a favorite of the specified user. - pub async fn set_favorite(&self, favorite: Option, user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn set_favorite(&self, favorite: Option, user_uuid: &str, conn: &DbConn) -> EmptyResult { match favorite { None => Ok(()), // No change requested. Some(status) => Favorite::set_favorite(status, &self.uuid, user_uuid, conn).await, } } - pub async fn get_folder_uuid(&self, user_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn get_folder_uuid(&self, user_uuid: &str, conn: &DbConn) -> Option { db_run! {conn: { folders_ciphers::table .inner_join(folders::table) @@ -568,13 +564,12 @@ impl Cipher { }} } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! {conn: { ciphers::table .filter(ciphers::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } @@ -590,7 +585,7 @@ impl Cipher { // true, then the non-interesting ciphers will not be returned. As a // result, those ciphers will not appear in "My Vault" for the org // owner/admin, but they can still be accessed via the org vault view. - pub async fn find_by_user(user_uuid: &str, visible_only: bool, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, visible_only: bool, conn: &DbConn) -> Vec { db_run! {conn: { let mut query = ciphers::table .left_join(ciphers_collections::table.on( @@ -633,28 +628,28 @@ impl Cipher { query .select(ciphers::all_columns) .distinct() - .load::(conn).expect("Error loading ciphers").from_db() + .load::(conn).expect("Error loading ciphers") }} } // Find all ciphers visible to the specified user. - pub async fn find_by_user_visible(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user_visible(user_uuid: &str, conn: &DbConn) -> Vec { Self::find_by_user(user_uuid, true, conn).await } // Find all ciphers directly owned by the specified user. - pub async fn find_owned_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_owned_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! {conn: { ciphers::table .filter( 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") }} } - pub async fn count_owned_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn count_owned_by_user(user_uuid: &str, conn: &DbConn) -> i64 { db_run! {conn: { ciphers::table .filter(ciphers::user_uuid.eq(user_uuid)) @@ -665,15 +660,15 @@ impl Cipher { }} } - pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec { 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") }} } - pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 { db_run! {conn: { ciphers::table .filter(ciphers::organization_uuid.eq(org_uuid)) @@ -684,25 +679,25 @@ impl Cipher { }} } - pub async fn find_by_folder(folder_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec { db_run! {conn: { 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") }} } /// Find all ciphers that were deleted before the specified datetime. - pub async fn find_deleted_before(dt: &NaiveDateTime, conn: &mut DbConn) -> Vec { + pub async fn find_deleted_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec { 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") }} } - pub async fn get_collections(&self, user_id: String, conn: &mut DbConn) -> Vec { + pub async fn get_collections(&self, user_id: String, conn: &DbConn) -> Vec { db_run! {conn: { ciphers_collections::table .inner_join(collections::table.on( @@ -731,7 +726,7 @@ impl Cipher { /// Return a Vec with (cipher_uuid, collection_uuid) /// This is used during a full sync so we only need one query for all collections accessible. - pub async fn get_collections_with_cipher_by_user(user_id: String, conn: &mut DbConn) -> Vec<(String, String)> { + pub async fn get_collections_with_cipher_by_user(user_id: String, conn: &DbConn) -> Vec<(String, String)> { db_run! {conn: { ciphers_collections::table .inner_join(collections::table.on( diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index f2b04ce5..84ad1ced 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}; +use super::{CollectionGroup, User, 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, - } +use crate::db::schema::{ciphers_collections, collections, users_collections}; - #[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, 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 = ciphers_collections)] - #[diesel(primary_key(cipher_uuid, collection_uuid))] - pub struct CollectionCipher { - pub cipher_uuid: String, - pub collection_uuid: String, - } +#[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 @@ -75,7 +75,7 @@ impl Collection { &self, user_uuid: &str, cipher_sync_data: Option<&crate::api::core::CipherSyncData>, - conn: &mut DbConn, + conn: &DbConn, ) -> Value { let (read_only, hide_passwords) = if let Some(cipher_sync_data) = cipher_sync_data { match cipher_sync_data.user_organizations.get(&self.org_uuid) { @@ -110,13 +110,13 @@ use crate::error::MapResult; /// Database methods impl Collection { - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &DbConn) -> EmptyResult { self.update_users_revision(conn).await; 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,19 +132,18 @@ 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") } } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { self.update_users_revision(conn).await; CollectionCipher::delete_all_by_collection(&self.uuid, conn).await?; CollectionUser::delete_all_by_collection(&self.uuid, conn).await?; @@ -157,30 +156,29 @@ impl Collection { }} } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { for collection in Self::find_by_organization(org_uuid, conn).await { collection.delete(conn).await?; } Ok(()) } - pub async fn update_users_revision(&self, conn: &mut DbConn) { + pub async fn update_users_revision(&self, conn: &DbConn) { for user_org in UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn).await.iter() { User::update_uuid_revision(&user_org.user_uuid, conn).await; } } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { collections::table .filter(collections::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_user_uuid(user_uuid: String, conn: &mut DbConn) -> Vec { + pub async fn find_by_user_uuid(user_uuid: String, conn: &DbConn) -> Vec { db_run! { conn: { collections::table .left_join(users_collections::table.on( @@ -220,22 +218,18 @@ impl Collection { ) .select(collections::all_columns) .distinct() - .load::(conn).expect("Error loading collections").from_db() + .load::(conn).expect("Error loading collections") }} } // Check if a user has access to a specific collection // FIXME: This needs to be reviewed. The query used by `find_by_user_uuid` could be adjusted to filter when needed. // For now this is a good solution without making to much changes. - pub async fn has_access_by_collection_and_user_uuid( - collection_uuid: &str, - user_uuid: &str, - conn: &mut DbConn, - ) -> bool { + pub async fn has_access_by_collection_and_user_uuid(collection_uuid: &str, user_uuid: &str, conn: &DbConn) -> bool { Self::find_by_user_uuid(user_uuid.to_owned(), conn).await.into_iter().any(|c| c.uuid == collection_uuid) } - pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec { Self::find_by_user_uuid(user_uuid.to_owned(), conn) .await .into_iter() @@ -243,17 +237,16 @@ impl Collection { .collect() } - pub async fn find_by_organization(org_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_organization(org_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { collections::table .filter(collections::org_uuid.eq(org_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading collections") - .from_db() }} } - pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 { db_run! { conn: { collections::table .filter(collections::org_uuid.eq(org_uuid)) @@ -264,19 +257,18 @@ impl Collection { }} } - pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { collections::table .filter(collections::uuid.eq(uuid)) .filter(collections::org_uuid.eq(org_uuid)) .select(collections::all_columns) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: String, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: String, conn: &DbConn) -> Option { db_run! { conn: { collections::table .left_join(users_collections::table.on( @@ -313,12 +305,11 @@ impl Collection { ) ) ).select(collections::all_columns) - .first::(conn).ok() - .from_db() + .first::(conn).ok() }} } - pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &DbConn) -> bool { let user_uuid = user_uuid.to_string(); db_run! { conn: { collections::table @@ -364,7 +355,7 @@ impl Collection { }} } - pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &DbConn) -> bool { let user_uuid = user_uuid.to_string(); db_run! { conn: { collections::table @@ -413,29 +404,27 @@ impl Collection { /// Database methods impl CollectionUser { - pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { users_collections::table .filter(users_collections::user_uuid.eq(user_uuid)) .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) .filter(collections::org_uuid.eq(org_uuid)) .select(users_collections::all_columns) - .load::(conn) + .load::(conn) .expect("Error loading users_collections") - .from_db() }} } - pub async fn find_by_organization(org_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_organization(org_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { users_collections::table .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) .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() }} } @@ -444,7 +433,7 @@ impl CollectionUser { collection_uuid: &str, read_only: bool, hide_passwords: bool, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { User::update_uuid_revision(user_uuid, conn).await; @@ -497,7 +486,7 @@ impl CollectionUser { } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; db_run! { conn: { @@ -511,60 +500,52 @@ impl CollectionUser { }} } - pub async fn find_by_collection(collection_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { 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() }} } pub async fn find_by_collection_swap_user_uuid_with_org_user_uuid( collection_uuid: &str, - conn: &mut DbConn, + conn: &DbConn, ) -> Vec { db_run! { conn: { users_collections::table .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() }} } - pub async fn find_by_collection_and_user( - collection_uuid: &str, - user_uuid: &str, - conn: &mut DbConn, - ) -> Option { + pub async fn find_by_collection_and_user(collection_uuid: &str, user_uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { users_collections::table .filter(users_collections::collection_uuid.eq(collection_uuid)) .filter(users_collections::user_uuid.eq(user_uuid)) .select(users_collections::all_columns) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { 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() }} } - pub async fn delete_all_by_collection(collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { for collection in CollectionUser::find_by_collection(collection_uuid, conn).await.iter() { User::update_uuid_revision(&collection.user_uuid, conn).await; } @@ -576,7 +557,7 @@ impl CollectionUser { }} } - pub async fn delete_all_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> EmptyResult { let collectionusers = Self::find_by_organization_and_user_uuid(org_uuid, user_uuid, conn).await; db_run! { conn: { @@ -595,7 +576,7 @@ impl CollectionUser { /// Database methods impl CollectionCipher { - pub async fn save(cipher_uuid: &str, collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult { Self::update_users_revision(collection_uuid, conn).await; db_run! { conn: @@ -625,7 +606,7 @@ impl CollectionCipher { } } - pub async fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult { Self::update_users_revision(collection_uuid, conn).await; db_run! { conn: { @@ -639,7 +620,7 @@ impl CollectionCipher { }} } - pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(ciphers_collections::table.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))) .execute(conn) @@ -647,7 +628,7 @@ impl CollectionCipher { }} } - pub async fn delete_all_by_collection(collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(ciphers_collections::table.filter(ciphers_collections::collection_uuid.eq(collection_uuid))) .execute(conn) @@ -655,7 +636,7 @@ impl CollectionCipher { }} } - pub async fn update_users_revision(collection_uuid: &str, conn: &mut DbConn) { + pub async fn update_users_revision(collection_uuid: &str, conn: &DbConn) { if let Some(collection) = Collection::find_by_uuid(collection_uuid, conn).await { collection.update_users_revision(conn).await; } diff --git a/src/db/models/device.rs b/src/db/models/device.rs index b80b47a1..2173feb8 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 @@ -115,27 +114,26 @@ use crate::error::MapResult; /// Database methods impl Device { - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { self.updated_at = Utc::now().naive_utc(); 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") } } } - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(devices::table.filter(devices::user_uuid.eq(user_uuid))) .execute(conn) @@ -143,38 +141,35 @@ impl Device { }} } - pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { devices::table .filter(devices::uuid.eq(uuid)) .filter(devices::user_uuid.eq(user_uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { devices::table .filter(devices::user_uuid.eq(user_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading devices") - .from_db() }} } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { devices::table .filter(devices::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn clear_push_token_by_uuid(uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn clear_push_token_by_uuid(uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::update(devices::table) .filter(devices::uuid.eq(uuid)) @@ -183,38 +178,35 @@ impl Device { .map_res("Error removing push token") }} } - pub async fn find_by_refresh_token(refresh_token: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_refresh_token(refresh_token: &str, conn: &DbConn) -> Option { db_run! { conn: { devices::table .filter(devices::refresh_token.eq(refresh_token)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_latest_active_by_user(user_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_latest_active_by_user(user_uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { 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 { + pub async fn find_push_devices_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { 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() }} } - pub async fn check_user_has_push_device(user_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn check_user_has_push_device(user_uuid: &str, conn: &DbConn) -> bool { db_run! { conn: { devices::table .filter(devices::user_uuid.eq(user_uuid)) diff --git a/src/db/models/emergency_access.rs b/src/db/models/emergency_access.rs index ccb21e5b..7b3259e3 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; +use crate::db::schema::emergency_access; -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, - } +#[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 @@ -66,7 +65,7 @@ impl EmergencyAccess { }) } - pub async fn to_json_grantor_details(&self, conn: &mut DbConn) -> Value { + pub async fn to_json_grantor_details(&self, conn: &DbConn) -> Value { let grantor_user = User::find_by_uuid(&self.grantor_uuid, conn).await.expect("Grantor user not found."); json!({ @@ -81,7 +80,7 @@ impl EmergencyAccess { }) } - pub async fn to_json_grantee_details(&self, conn: &mut DbConn) -> Value { + pub async fn to_json_grantee_details(&self, conn: &DbConn) -> Value { let grantee_user = if let Some(grantee_uuid) = self.grantee_uuid.as_deref() { Some(User::find_by_uuid(grantee_uuid, conn).await.expect("Grantee user not found.")) } else if let Some(email) = self.email.as_deref() { @@ -130,22 +129,22 @@ pub enum EmergencyAccessStatus { // region Database methods impl EmergencyAccess { - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&self.grantor_uuid, conn).await; self.updated_at = Utc::now().naive_utc(); 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") } @@ -169,7 +167,7 @@ impl EmergencyAccess { &mut self, status: i32, date: &NaiveDateTime, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { // Update the grantee so that it will refresh it's status. User::update_uuid_revision(self.grantee_uuid.as_ref().expect("Error getting grantee"), conn).await; @@ -178,33 +176,29 @@ 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") }} } - pub async fn update_last_notification_date_and_save( - &mut self, - date: &NaiveDateTime, - conn: &mut DbConn, - ) -> EmptyResult { + pub async fn update_last_notification_date_and_save(&mut self, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { self.last_notification_at = Some(date.to_owned()); self.updated_at = date.to_owned(); 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") }} } - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { for ea in Self::find_all_by_grantor_uuid(user_uuid, conn).await { ea.delete(conn).await?; } @@ -214,22 +208,22 @@ impl EmergencyAccess { Ok(()) } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { 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") }} } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &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() }} } @@ -237,59 +231,59 @@ impl EmergencyAccess { grantor_uuid: &str, grantee_uuid: &str, email: &str, - conn: &mut DbConn, + conn: &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 { + pub async fn find_all_recoveries_initiated(conn: &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 { + pub async fn find_by_uuid_and_grantor_uuid(uuid: &str, grantor_uuid: &str, conn: &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 { + pub async fn find_all_by_grantee_uuid(grantee_uuid: &str, conn: &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 { + pub async fn find_invited_by_grantee_email(grantee_email: &str, conn: &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 { + pub async fn find_all_by_grantor_uuid(grantor_uuid: &str, conn: &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 af2f6c66..26f45997 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 @@ -178,27 +177,27 @@ impl Event { /// ############# /// Basic Queries - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &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") } } } - pub async fn save_user_event(events: Vec, conn: &mut DbConn) -> EmptyResult { + pub async fn save_user_event(events: Vec, conn: &DbConn) -> EmptyResult { // Special save function which is able to handle multiple events. // SQLite doesn't support the DEFAULT argument, and does not support inserting multiple values at the same time. // MySQL and PostgreSQL do. @@ -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) @@ -235,9 +232,9 @@ impl Event { } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &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") }} @@ -249,24 +246,23 @@ impl Event { org_uuid: &str, start: &NaiveDateTime, end: &NaiveDateTime, - conn: &mut DbConn, + conn: &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 { + pub async fn count_by_org(org_uuid: &str, conn: &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() @@ -279,20 +275,19 @@ impl Event { user_org_uuid: &str, start: &NaiveDateTime, end: &NaiveDateTime, - conn: &mut DbConn, + conn: &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() }} } @@ -300,25 +295,24 @@ impl Event { cipher_uuid: &str, start: &NaiveDateTime, end: &NaiveDateTime, - conn: &mut DbConn, + conn: &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() }} } - pub async fn clean_events(conn: &mut DbConn) -> EmptyResult { + pub async fn clean_events(conn: &DbConn) -> EmptyResult { 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 a301f597..c78641a1 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; @@ -17,7 +15,7 @@ use crate::error::MapResult; impl Favorite { // Returns whether the specified cipher is a favorite of the specified user. - pub async fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> bool { db_run! { conn: { let query = favorites::table .filter(favorites::cipher_uuid.eq(cipher_uuid)) @@ -29,7 +27,7 @@ impl Favorite { } // Sets whether the specified cipher is a favorite of the specified user. - pub async fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> EmptyResult { let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, conn).await, favorite); match (old, new) { (false, true) => { @@ -62,18 +60,18 @@ 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 { + pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &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") }} } // Delete all favorite entries associated with the specified user. - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &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") }} @@ -81,9 +79,9 @@ impl Favorite { /// Return a vec with (cipher_uuid) this will only contain favorite flagged ciphers /// 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 { + pub async fn get_all_cipher_uuid_by_user(user_uuid: &str, conn: &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 9385e78d..2986da0f 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}; -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, 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 @@ -67,14 +66,14 @@ use crate::error::MapResult; /// Database methods impl Folder { - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; self.updated_at = Utc::now().naive_utc(); 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,19 +89,18 @@ 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") } } } - pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; FolderCipher::delete_all_by_folder(&self.uuid, conn).await?; @@ -113,49 +111,47 @@ impl Folder { }} } - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { for folder in Self::find_by_user(user_uuid, conn).await { folder.delete(conn).await?; } Ok(()) } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { folders::table .filter(folders::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { folders::table .filter(folders::user_uuid.eq(user_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading folders") - .from_db() }} } } impl FolderCipher { - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &DbConn) -> EmptyResult { db_run! { conn: sqlite, mysql { // Not checking for ForeignKey Constraints here. // 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) @@ -164,7 +160,7 @@ impl FolderCipher { } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete( folders_ciphers::table @@ -176,7 +172,7 @@ impl FolderCipher { }} } - pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(folders_ciphers::table.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))) .execute(conn) @@ -184,7 +180,7 @@ impl FolderCipher { }} } - pub async fn delete_all_by_folder(folder_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(folders_ciphers::table.filter(folders_ciphers::folder_uuid.eq(folder_uuid))) .execute(conn) @@ -192,30 +188,28 @@ impl FolderCipher { }} } - pub async fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { 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() }} } - pub async fn find_by_folder(folder_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { folders_ciphers::table .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading folders") - .from_db() }} } /// Return a vec with (cipher_uuid, folder_uuid) /// This is used during a full sync so we only need one query for all folder matches. - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<(String, String)> { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<(String, String)> { db_run! { conn: { folders_ciphers::table .inner_join(folders::table) diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 670e3114..aeb31f9c 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 @@ -69,7 +68,7 @@ impl Group { }) } - pub async fn to_json_details(&self, conn: &mut DbConn) -> Value { + pub async fn to_json_details(&self, conn: &DbConn) -> Value { let collections_groups: Vec = CollectionGroup::find_by_group(&self.uuid, conn) .await .iter() @@ -131,13 +130,13 @@ use super::{User, UserOrganization}; /// Database methods impl Group { - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { self.revision_date = Utc::now().naive_utc(); 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,36 +152,34 @@ 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") } } } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { for group in Self::find_by_organization(org_uuid, conn).await { group.delete(conn).await?; } Ok(()) } - pub async fn find_by_organization(organizations_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_organization(organizations_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { groups::table .filter(groups::organizations_uuid.eq(organizations_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading groups") - .from_db() }} } - pub async fn count_by_org(organizations_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn count_by_org(organizations_uuid: &str, conn: &DbConn) -> i64 { db_run! { conn: { groups::table .filter(groups::organizations_uuid.eq(organizations_uuid)) @@ -193,27 +190,25 @@ impl Group { }} } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { groups::table .filter(groups::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_external_id(id: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_external_id(id: &str, conn: &DbConn) -> Option { 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 - pub async fn gather_user_organizations_full_access(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn gather_user_organizations_full_access(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { groups_users::table .inner_join(users_organizations::table.on( @@ -231,7 +226,7 @@ impl Group { }} } - pub async fn is_in_full_access_group(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn is_in_full_access_group(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> bool { db_run! { conn: { groups::table .inner_join(groups_users::table.on( @@ -249,7 +244,7 @@ impl Group { }} } - pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { CollectionGroup::delete_all_by_group(&self.uuid, conn).await?; GroupUser::delete_all_by_group(&self.uuid, conn).await?; @@ -260,13 +255,13 @@ impl Group { }} } - pub async fn update_revision(uuid: &str, conn: &mut DbConn) { + pub async fn update_revision(uuid: &str, conn: &DbConn) { if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await { warn!("Failed to update revision for {}: {:#?}", uuid, e); } } - async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &mut DbConn) -> EmptyResult { + async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { db_run! {conn: { crate::util::retry(|| { diesel::update(groups::table.filter(groups::uuid.eq(uuid))) @@ -279,7 +274,7 @@ impl Group { } impl CollectionGroup { - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { let group_users = GroupUser::find_by_group(&self.groups_uuid, conn).await; for group_user in group_users { group_user.update_user_revision(conn).await; @@ -334,17 +329,16 @@ impl CollectionGroup { } } - pub async fn find_by_group(group_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_group(group_uuid: &str, conn: &DbConn) -> Vec { 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() }} } - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { collections_groups::table .inner_join(groups_users::table.on( @@ -355,24 +349,22 @@ 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() }} } - pub async fn find_by_collection(collection_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { 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() }} } - pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { let group_users = GroupUser::find_by_group(&self.groups_uuid, conn).await; for group_user in group_users { group_user.update_user_revision(conn).await; @@ -387,7 +379,7 @@ impl CollectionGroup { }} } - pub async fn delete_all_by_group(group_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_group(group_uuid: &str, conn: &DbConn) -> EmptyResult { let group_users = GroupUser::find_by_group(group_uuid, conn).await; for group_user in group_users { group_user.update_user_revision(conn).await; @@ -401,7 +393,7 @@ impl CollectionGroup { }} } - pub async fn delete_all_by_collection(collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { let collection_assigned_to_groups = CollectionGroup::find_by_collection(collection_uuid, conn).await; for collection_assigned_to_group in collection_assigned_to_groups { let group_users = GroupUser::find_by_group(&collection_assigned_to_group.groups_uuid, conn).await; @@ -420,7 +412,7 @@ impl CollectionGroup { } impl GroupUser { - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { self.update_user_revision(conn).await; db_run! { conn: @@ -466,27 +458,25 @@ impl GroupUser { } } - pub async fn find_by_group(group_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_group(group_uuid: &str, conn: &DbConn) -> Vec { 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() }} } - pub async fn find_by_user(users_organizations_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(users_organizations_uuid: &str, conn: &DbConn) -> Vec { 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() }} } - pub async fn update_user_revision(&self, conn: &mut DbConn) { + pub async fn update_user_revision(&self, conn: &DbConn) { match UserOrganization::find_by_uuid(&self.users_organizations_uuid, conn).await { Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, None => warn!("User could not be found!"), @@ -496,7 +486,7 @@ impl GroupUser { pub async fn delete_by_group_id_and_user_id( group_uuid: &str, users_organizations_uuid: &str, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { match UserOrganization::find_by_uuid(users_organizations_uuid, conn).await { Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, @@ -512,7 +502,7 @@ impl GroupUser { }} } - pub async fn delete_all_by_group(group_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_group(group_uuid: &str, conn: &DbConn) -> EmptyResult { let group_users = GroupUser::find_by_group(group_uuid, conn).await; for group_user in group_users { group_user.update_user_revision(conn).await; @@ -526,7 +516,7 @@ impl GroupUser { }} } - pub async fn delete_all_by_user(users_organizations_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(users_organizations_uuid: &str, conn: &DbConn) -> EmptyResult { match UserOrganization::find_by_uuid(users_organizations_uuid, conn).await { Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, None => warn!("User could not be found!"), diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 8b3f1271..92ac59fa 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 @@ -90,11 +89,11 @@ impl OrgPolicy { /// Database methods impl OrgPolicy { - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &DbConn) -> EmptyResult { 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,17 +121,17 @@ 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") } } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(org_policies::table.filter(org_policies::uuid.eq(self.uuid))) .execute(conn) @@ -141,27 +139,25 @@ impl OrgPolicy { }} } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { org_policies::table .filter(org_policies::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec { 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() }} } - pub async fn find_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { org_policies::table .inner_join( @@ -173,24 +169,22 @@ 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() }} } - pub async fn find_by_org_and_type(org_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> Option { + pub async fn find_by_org_and_type(org_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> Option { db_run! { conn: { 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() }} } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(org_policies::table.filter(org_policies::org_uuid.eq(org_uuid))) .execute(conn) @@ -201,7 +195,7 @@ impl OrgPolicy { pub async fn find_accepted_and_confirmed_by_user_and_active_policy( user_uuid: &str, policy_type: OrgPolicyType, - conn: &mut DbConn, + conn: &DbConn, ) -> Vec { db_run! { conn: { org_policies::table @@ -219,16 +213,15 @@ 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() }} } pub async fn find_confirmed_by_user_and_active_policy( user_uuid: &str, policy_type: OrgPolicyType, - conn: &mut DbConn, + conn: &DbConn, ) -> Vec { db_run! { conn: { org_policies::table @@ -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() }} } @@ -256,7 +248,7 @@ impl OrgPolicy { user_uuid: &str, policy_type: OrgPolicyType, exclude_org_uuid: Option<&str>, - conn: &mut DbConn, + conn: &DbConn, ) -> bool { for policy in OrgPolicy::find_accepted_and_confirmed_by_user_and_active_policy(user_uuid, policy_type, conn).await @@ -279,7 +271,7 @@ impl OrgPolicy { user_uuid: &str, org_uuid: &str, exclude_current_org: bool, - conn: &mut DbConn, + conn: &DbConn, ) -> OrgPolicyResult { // Enforce TwoFactor/TwoStep login if TwoFactor::find_by_user(user_uuid, conn).await.is_empty() { @@ -305,7 +297,7 @@ impl OrgPolicy { Ok(()) } - pub async fn org_is_reset_password_auto_enroll(org_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn org_is_reset_password_auto_enroll(org_uuid: &str, conn: &DbConn) -> bool { match OrgPolicy::find_by_org_and_type(org_uuid, OrgPolicyType::ResetPassword, conn).await { Some(policy) => match serde_json::from_str::>(&policy.data) { Ok(opts) => { @@ -321,7 +313,7 @@ impl OrgPolicy { /// Returns true if the user belongs to an org that has enabled the `DisableHideEmail` /// option of the `Send Options` policy, and the user is not an owner or admin of that org. - pub async fn is_hide_email_disabled(user_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn is_hide_email_disabled(user_uuid: &str, conn: &DbConn) -> bool { for policy in OrgPolicy::find_confirmed_by_user_and_active_policy(user_uuid, OrgPolicyType::SendOptions, conn).await { diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index 534dbce8..a9b3754d 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -4,46 +4,45 @@ 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 = 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, +#[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, - pub external_id: Option, - } + pub access_all: bool, + pub akey: String, + pub status: i32, + pub atype: i32, + pub reset_password_key: Option, + pub external_id: 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 = 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 @@ -267,7 +266,7 @@ use crate::error::MapResult; /// Database methods impl Organization { - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &DbConn) -> EmptyResult { if !email_address::EmailAddress::is_valid(self.billing_email.trim()) { err!(format!("BillingEmail {} is not a valid email address", self.billing_email.trim())) } @@ -279,7 +278,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(()), @@ -287,7 +286,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") } @@ -296,19 +295,18 @@ 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") } } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { use super::{Cipher, Collection}; Cipher::delete_all_by_organization(&self.uuid, conn).await?; @@ -324,24 +322,24 @@ impl Organization { }} } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { 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 { + pub async fn get_all(conn: &DbConn) -> Vec { db_run! { conn: { - organizations::table.load::(conn).expect("Error loading organizations").from_db() + organizations::table.load::(conn).expect("Error loading organizations") }} } } impl UserOrganization { - pub async fn to_json(&self, conn: &mut DbConn) -> Value { + pub async fn to_json(&self, conn: &DbConn) -> Value { let org = Organization::find_by_uuid(&self.org_uuid, conn).await.unwrap(); // https://github.com/bitwarden/server/blob/13d1e74d6960cf0d042620b72d85bf583a4236f7/src/Api/Models/Response/ProfileOrganizationResponseModel.cs @@ -406,12 +404,7 @@ impl UserOrganization { }) } - pub async fn to_json_user_details( - &self, - include_collections: bool, - include_groups: bool, - conn: &mut DbConn, - ) -> Value { + pub async fn to_json_user_details(&self, include_collections: bool, include_groups: bool, conn: &DbConn) -> Value { let user = User::find_by_uuid(&self.user_uuid, conn).await.unwrap(); // Because BitWarden want the status to be -1 for revoked users we need to catch that here. @@ -475,7 +468,7 @@ impl UserOrganization { }) } - pub async fn to_json_details(&self, conn: &mut DbConn) -> Value { + pub async fn to_json_details(&self, conn: &DbConn) -> Value { let coll_uuids = if self.access_all { vec![] // If we have complete access, no need to fill the array } else { @@ -513,13 +506,13 @@ impl UserOrganization { "Object": "organizationUserDetails", }) } - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; db_run! { conn: sqlite, mysql { match diesel::replace_into(users_organizations::table) - .values(UserOrganizationDb::to_db(self)) + .values(self) .execute(conn) { Ok(_) => Ok(()), @@ -527,7 +520,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") }, @@ -535,19 +528,18 @@ 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") } } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; CollectionUser::delete_all_by_user_and_org(&self.user_uuid, &self.org_uuid, conn).await?; @@ -560,21 +552,21 @@ impl UserOrganization { }} } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { for user_org in Self::find_by_org(org_uuid, conn).await { user_org.delete(conn).await?; } Ok(()) } - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { for user_org in Self::find_any_state_by_user(user_uuid, conn).await { user_org.delete(conn).await?; } Ok(()) } - pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &DbConn) -> Option { if let Some(user) = super::User::find_by_mail(email, conn).await { if let Some(user_org) = UserOrganization::find_by_user_and_org(&user.uuid, org_id, conn).await { return Some(user_org); @@ -596,55 +588,55 @@ impl UserOrganization { (self.access_all || self.atype >= UserOrgType::Admin) && self.has_status(UserOrgStatus::Confirmed) } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { users_organizations::table .filter(users_organizations::uuid.eq(uuid)) - .first::(conn) - .ok().from_db() + .first::(conn) + .ok() }} } - pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { 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() }} } - pub async fn find_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { 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() }} } - pub async fn find_invited_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_invited_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { 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() }} } - pub async fn find_any_state_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_any_state_by_user(user_uuid: &str, conn: &DbConn) -> Vec { 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() }} } - pub async fn count_accepted_and_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn count_accepted_and_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> i64 { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -656,16 +648,16 @@ impl UserOrganization { }} } - pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec { 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") }} } - pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { + pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -676,17 +668,17 @@ impl UserOrganization { }} } - pub async fn find_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &mut DbConn) -> Vec { + pub async fn find_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &DbConn) -> Vec { db_run! { conn: { 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") }} } - pub async fn count_confirmed_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &mut DbConn) -> i64 { + pub async fn count_confirmed_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &DbConn) -> i64 { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -698,26 +690,26 @@ impl UserOrganization { }} } - pub async fn find_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { 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() }} } - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { 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") }} } - pub async fn get_org_uuid_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn get_org_uuid_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -727,7 +719,7 @@ impl UserOrganization { }} } - pub async fn find_by_user_and_policy(user_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> Vec { + pub async fn find_by_user_and_policy(user_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> Vec { db_run! { conn: { users_organizations::table .inner_join( @@ -741,12 +733,12 @@ 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() }} } - pub async fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -765,11 +757,11 @@ impl UserOrganization { ) .select(users_organizations::all_columns) .distinct() - .load::(conn).expect("Error loading user organizations").from_db() + .load::(conn).expect("Error loading user organizations") }} } - pub async fn user_has_ge_admin_access_to_cipher(user_uuid: &str, cipher_uuid: &str, conn: &mut DbConn) -> bool { + pub async fn user_has_ge_admin_access_to_cipher(user_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> bool { db_run! { conn: { users_organizations::table .inner_join(ciphers::table.on(ciphers::uuid.eq(cipher_uuid).and(ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())))) @@ -781,7 +773,7 @@ impl UserOrganization { }} } - pub async fn find_by_collection_and_org(collection_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_collection_and_org(collection_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -794,18 +786,18 @@ impl UserOrganization { ) ) .select(users_organizations::all_columns) - .load::(conn).expect("Error loading user organizations").from_db() + .load::(conn).expect("Error loading user organizations") }} } - pub async fn find_by_external_id_and_org(ext_id: &str, org_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_external_id_and_org(ext_id: &str, org_uuid: &str, conn: &DbConn) -> Option { db_run! {conn: { users_organizations::table .filter( users_organizations::external_id.eq(ext_id) .and(users_organizations::org_uuid.eq(org_uuid)) ) - .first::(conn).ok().from_db() + .first::(conn).ok() }} } } @@ -815,7 +807,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(()), @@ -823,7 +815,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") } @@ -832,12 +824,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") } @@ -848,8 +839,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 49756125..6b90ec80 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; +use crate::db::schema::sends; -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, +#[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 user_uuid: Option, + pub organization_uuid: Option, + pub name: String, + pub notes: 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 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 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 creation_date: NaiveDateTime, - pub revision_date: NaiveDateTime, - pub expiration_date: Option, - pub deletion_date: NaiveDateTime, - - pub disabled: bool, - pub hide_email: Option, - } + pub disabled: bool, + pub hide_email: Option, } #[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)] @@ -101,7 +99,7 @@ impl Send { } } - pub async fn creator_identifier(&self, conn: &mut DbConn) -> Option { + pub async fn creator_identifier(&self, conn: &DbConn) -> Option { if let Some(hide_email) = self.hide_email { if hide_email { return None; @@ -148,7 +146,7 @@ impl Send { }) } - pub async fn to_json_access(&self, conn: &mut DbConn) -> Value { + pub async fn to_json_access(&self, conn: &DbConn) -> Value { use crate::util::format_date; let data: Value = serde_json::from_str(&self.data).unwrap_or_default(); @@ -174,14 +172,14 @@ use crate::api::EmptyResult; use crate::error::MapResult; impl Send { - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { self.update_users_revision(conn).await; self.revision_date = Utc::now().naive_utc(); 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,19 +195,18 @@ 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") } } } - pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { self.update_users_revision(conn).await; if self.atype == SendType::File as i32 { @@ -224,13 +221,13 @@ impl Send { } /// Purge all sends that are past their deletion date. - pub async fn purge(conn: &mut DbConn) { + pub async fn purge(conn: &DbConn) { for send in Self::find_by_past_deletion_date(conn).await { send.delete(conn).await.ok(); } } - pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec { + pub async fn update_users_revision(&self, conn: &DbConn) -> Vec { let mut user_uuids = Vec::new(); match &self.user_uuid { Some(user_uuid) => { @@ -244,14 +241,14 @@ impl Send { user_uuids } - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { for send in Self::find_by_user(user_uuid, conn).await { send.delete(conn).await?; } Ok(()) } - pub async fn find_by_access_id(access_id: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_access_id(access_id: &str, conn: &DbConn) -> Option { use data_encoding::BASE64URL_NOPAD; use uuid::Uuid; @@ -268,38 +265,37 @@ impl Send { Self::find_by_uuid(&uuid, conn).await } - pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { db_run! {conn: { sends::table .filter(sends::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { 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") }} } - pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec { 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") }} } - pub async fn find_by_past_deletion_date(conn: &mut DbConn) -> Vec { + pub async fn find_by_past_deletion_date(conn: &DbConn) -> Vec { let now = Utc::now().naive_utc(); 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 ef03979a..5ab466ac 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)] @@ -68,11 +67,11 @@ impl TwoFactor { /// Database methods impl TwoFactor { - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &DbConn) -> EmptyResult { 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,17 +95,17 @@ 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") } } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(twofactor::table.filter(twofactor::uuid.eq(self.uuid))) .execute(conn) @@ -115,29 +113,27 @@ impl TwoFactor { }} } - pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { db_run! { conn: { 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() }} } - pub async fn find_by_user_and_type(user_uuid: &str, atype: i32, conn: &mut DbConn) -> Option { + pub async fn find_by_user_and_type(user_uuid: &str, atype: i32, conn: &DbConn) -> Option { db_run! { conn: { twofactor::table .filter(twofactor::user_uuid.eq(user_uuid)) .filter(twofactor::atype.eq(atype)) - .first::(conn) + .first::(conn) .ok() - .from_db() }} } - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid))) .execute(conn) @@ -145,13 +141,12 @@ impl TwoFactor { }} } - pub async fn migrate_u2f_to_webauthn(conn: &mut DbConn) -> EmptyResult { + pub async fn migrate_u2f_to_webauthn(conn: &DbConn) -> EmptyResult { 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 49f7691f..597e3d2d 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 { @@ -24,7 +23,7 @@ impl TwoFactorIncomplete { device_uuid: &str, device_name: &str, ip: &ClientIp, - conn: &mut DbConn, + conn: &DbConn, ) -> EmptyResult { if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() { return Ok(()); @@ -52,7 +51,7 @@ impl TwoFactorIncomplete { }} } - pub async fn mark_complete(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn mark_complete(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> EmptyResult { if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() { return Ok(()); } @@ -60,32 +59,30 @@ impl TwoFactorIncomplete { Self::delete_by_user_and_device(user_uuid, device_uuid, conn).await } - pub async fn find_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> Option { db_run! { conn: { 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() }} } - pub async fn find_logins_before(dt: &NaiveDateTime, conn: &mut DbConn) -> Vec { + pub async fn find_logins_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec { db_run! {conn: { twofactor_incomplete::table .filter(twofactor_incomplete::login_time.lt(dt)) - .load::(conn) + .load::(conn) .expect("Error loading twofactor_incomplete") - .from_db() }} } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { Self::delete_by_user_and_device(&self.user_uuid, &self.device_uuid, conn).await } - pub async fn delete_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(twofactor_incomplete::table .filter(twofactor_incomplete::user_uuid.eq(user_uuid)) @@ -95,7 +92,7 @@ impl TwoFactorIncomplete { }} } - pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(twofactor_incomplete::table.filter(twofactor_incomplete::user_uuid.eq(user_uuid))) .execute(conn) diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 1475d637..5291f7b0 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, +use crate::db::schema::{invitations, users}; - pub email: String, - pub email_new: Option, - pub email_new_token: Option, - pub name: String, +#[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 password_hash: Vec, - pub salt: Vec, - pub password_iterations: i32, - pub password_hint: Option, + pub email: String, + pub email_new: Option, + pub email_new_token: Option, + pub name: String, - pub akey: String, - pub private_key: Option, - pub public_key: Option, + pub password_hash: Vec, + pub salt: Vec, + pub password_iterations: i32, + pub password_hint: 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 akey: String, + pub private_key: Option, + pub public_key: Option, - pub security_stamp: String, - pub stamp_exception: 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 equivalent_domains: String, - pub excluded_globals: String, + pub security_stamp: String, + pub stamp_exception: Option, - pub client_kdf_type: i32, - pub client_kdf_iter: i32, - pub client_kdf_memory: Option, - pub client_kdf_parallelism: Option, + pub equivalent_domains: String, + pub excluded_globals: String, - pub api_key: Option, + pub client_kdf_type: i32, + pub client_kdf_iter: i32, + pub client_kdf_memory: Option, + pub client_kdf_parallelism: Option, - pub avatar_color: Option, + pub api_key: Option, - pub external_id: Option, // Todo: Needs to be removed in the future, this is not used anymore. - } + pub avatar_color: Option, - #[derive(Identifiable, Queryable, Insertable)] - #[diesel(table_name = invitations)] - #[diesel(primary_key(email))] - pub struct Invitation { - pub email: String, - } + pub external_id: Option, // Todo: Needs to be removed in the future, this is not used anymore. +} + +#[derive(Identifiable, Queryable, Insertable)] +#[diesel(table_name = invitations)] +#[diesel(primary_key(email))] +pub struct Invitation { + pub email: String, } pub enum UserKdfType { @@ -224,7 +224,7 @@ use crate::error::MapResult; /// Database methods impl User { - pub async fn to_json(&self, conn: &mut DbConn) -> Value { + pub async fn to_json(&self, conn: &DbConn) -> Value { let mut orgs_json = Vec::new(); for c in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { orgs_json.push(c.to_json(conn).await); @@ -261,7 +261,7 @@ impl User { }) } - pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { if self.email.trim().is_empty() { err!("User email can't be empty") } @@ -271,7 +271,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(()), @@ -279,7 +279,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") } @@ -287,19 +287,18 @@ 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") } } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { for user_org in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { if user_org.atype == UserOrgType::Owner && UserOrganization::count_confirmed_by_org_and_type(&user_org.org_uuid, UserOrgType::Owner, conn).await @@ -327,13 +326,13 @@ impl User { }} } - pub async fn update_uuid_revision(uuid: &str, conn: &mut DbConn) { + pub async fn update_uuid_revision(uuid: &str, conn: &DbConn) { if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await { warn!("Failed to update revision for {}: {:#?}", uuid, e); } } - pub async fn update_all_revisions(conn: &mut DbConn) -> EmptyResult { + pub async fn update_all_revisions(conn: &DbConn) -> EmptyResult { let updated_at = Utc::now().naive_utc(); db_run! {conn: { @@ -346,13 +345,13 @@ impl User { }} } - pub async fn update_revision(&mut self, conn: &mut DbConn) -> EmptyResult { + pub async fn update_revision(&mut self, conn: &DbConn) -> EmptyResult { self.updated_at = Utc::now().naive_utc(); Self::_update_revision(&self.uuid, &self.updated_at, conn).await } - async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &mut DbConn) -> EmptyResult { + async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { db_run! {conn: { crate::util::retry(|| { diesel::update(users::table.filter(users::uuid.eq(uuid))) @@ -363,30 +362,30 @@ impl User { }} } - pub async fn find_by_mail(mail: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option { let lower_mail = mail.to_lowercase(); 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 { + pub async fn find_by_uuid(uuid: &str, conn: &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 get_all(conn: &mut DbConn) -> Vec { + pub async fn get_all(conn: &DbConn) -> Vec { db_run! {conn: { - users::table.load::(conn).expect("Error loading users").from_db() + users::table.load::(conn).expect("Error loading users") }} } - pub async fn last_active(&self, conn: &mut DbConn) -> Option { + pub async fn last_active(&self, conn: &DbConn) -> Option { match Device::find_latest_active_by_user(&self.uuid, conn).await { Some(device) => Some(device.updated_at), None => None, @@ -402,7 +401,7 @@ impl Invitation { } } - pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { + pub async fn save(&self, conn: &DbConn) -> EmptyResult { if self.email.trim().is_empty() { err!("Invitation email can't be empty") } @@ -412,13 +411,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) @@ -427,7 +426,7 @@ impl Invitation { } } - pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { + pub async fn delete(self, conn: &DbConn) -> EmptyResult { db_run! {conn: { diesel::delete(invitations::table.filter(invitations::email.eq(self.email))) .execute(conn) @@ -435,18 +434,18 @@ impl Invitation { }} } - pub async fn find_by_mail(mail: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option { let lower_mail = mail.to_lowercase(); db_run! {conn: { invitations::table .filter(invitations::email.eq(lower_mail)) - .first::(conn) + .first::(conn) .ok() - .from_db() + }} } - pub async fn take(mail: &str, conn: &mut DbConn) -> bool { + pub async fn take(mail: &str, conn: &DbConn) -> bool { match Self::find_by_mail(mail, conn).await { Some(invitation) => invitation.delete(conn).await.is_ok(), None => false, diff --git a/src/db/schemas/mysql/schema.rs b/src/db/schemas/mysql/schema.rs deleted file mode 100644 index f1a001fd..00000000 --- a/src/db/schemas/mysql/schema.rs +++ /dev/null @@ -1,362 +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, - external_id -> 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 -> Nullable, - master_password_hash -> Nullable, - 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!(users_organizations -> ciphers (org_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/schema.rs similarity index 100% rename from src/db/schemas/postgresql/schema.rs rename to src/db/schemas/schema.rs diff --git a/src/db/schemas/sqlite/schema.rs b/src/db/schemas/sqlite/schema.rs deleted file mode 100644 index 64786fb9..00000000 --- a/src/db/schemas/sqlite/schema.rs +++ /dev/null @@ -1,362 +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, - external_id -> 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 -> Nullable, - master_password_hash -> Nullable, - 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!(users_organizations -> ciphers (org_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/main.rs b/src/main.rs index 6c3593df..66fecdcc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -115,7 +115,7 @@ async fn main() -> Result<(), Error> { let pool = create_db_pool().await; schedule_jobs(pool.clone()); - crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&mut pool.get().await.unwrap()).await.unwrap(); + crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&pool.get().await.unwrap()).await.unwrap(); launch_rocket(pool, extra_debug).await // Blocks until program termination. }