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. }