diff --git a/Cargo.lock b/Cargo.lock index 8c9f1122..88851fab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,9 +207,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] @@ -309,12 +309,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ "iana-time-zone", - "js-sys", "num-integer", "num-traits", "serde", - "time 0.1.43", - "wasm-bindgen", "winapi", ] @@ -359,11 +356,11 @@ dependencies = [ "base64", "hkdf", "hmac", - "percent-encoding 2.1.0", + "percent-encoding", "rand", "sha2", "subtle", - "time 0.3.14", + "time", "version_check", ] @@ -374,13 +371,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd" dependencies = [ "cookie", - "idna 0.2.3", + "idna", "log", "publicsuffix", "serde", "serde_json", - "time 0.3.14", - "url 2.2.2", + "time", + "url", ] [[package]] @@ -565,27 +562,30 @@ dependencies = [ [[package]] name = "diesel" -version = "1.4.8" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" +checksum = "01e2adfd0a7a81070ed7beec0c62636458926326c16fedb77796d41e447b282d" dependencies = [ "bitflags", "byteorder", "chrono", "diesel_derives", + "itoa", "libsqlite3-sys", "mysqlclient-sys", + "percent-encoding", "pq-sys", "r2d2", - "url 1.7.2", + "url", ] [[package]] name = "diesel_derives" -version = "1.4.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +checksum = "22a7ab9d7967e6a1a247ea38aedf88ab808b4ac0c159576bc71866ab8f9f9250" dependencies = [ + "proc-macro-error", "proc-macro2", "quote", "syn", @@ -593,10 +593,11 @@ dependencies = [ [[package]] name = "diesel_migrations" -version = "1.4.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" +checksum = "e9ae22beef5e9d6fab9225ddb073c1c6c1a7a6ded5019d5da11d1e5c5adc34e2" dependencies = [ + "diesel", "migrations_internals", "migrations_macros", ] @@ -764,7 +765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ "matches", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] @@ -1125,17 +1126,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.2.3" @@ -1258,7 +1248,7 @@ dependencies = [ "futures-util", "hostname", "httpdate", - "idna 0.2.3", + "idna", "mime", "native-tls", "nom", @@ -1288,9 +1278,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.22.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d" +checksum = "9f0455f2c1bc9a7caa792907026e469c1d91761fb0ea37cbb16427c77280cf35" dependencies = [ "cc", "pkg-config", @@ -1384,23 +1374,23 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "migrations_internals" -version = "1.4.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" +checksum = "c493c09323068c01e54c685f7da41a9ccf9219735c3766fbfd6099806ea08fbc" dependencies = [ - "diesel", + "serde", + "toml", ] [[package]] name = "migrations_macros" -version = "1.4.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" +checksum = "8a8ff27a350511de30cdabb77147501c36ef02e0451d957abea2f30caffb2b58" dependencies = [ "migrations_internals", "proc-macro2", "quote", - "syn", ] [[package]] @@ -1732,12 +1722,6 @@ dependencies = [ "base64", ] -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - [[package]] name = "percent-encoding" version = "2.1.0" @@ -1878,6 +1862,30 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1918,7 +1926,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aeeedb0b429dc462f30ad27ef3de97058b060016f47790c066757be38ef792b4" dependencies = [ - "idna 0.2.3", + "idna", "psl-types", ] @@ -2109,7 +2117,7 @@ dependencies = [ "log", "mime", "native-tls", - "percent-encoding 2.1.0", + "percent-encoding", "pin-project-lite", "proc-macro-hack", "serde", @@ -2121,7 +2129,7 @@ dependencies = [ "tokio-util", "tower-service", "trust-dns-resolver", - "url 2.2.2", + "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2204,7 +2212,7 @@ dependencies = [ "serde_json", "state", "tempfile", - "time 0.3.14", + "time", "tokio", "tokio-stream", "tokio-util", @@ -2244,7 +2252,7 @@ dependencies = [ "log", "memchr", "pear", - "percent-encoding 2.1.0", + "percent-encoding", "pin-project-lite", "ref-cast", "rustls", @@ -2253,7 +2261,7 @@ dependencies = [ "smallvec", "stable-pattern", "state", - "time 0.3.14", + "time", "tokio", "tokio-rustls", "uncased", @@ -2484,7 +2492,7 @@ dependencies = [ "num-bigint", "num-traits", "thiserror", - "time 0.3.14", + "time", ] [[package]] @@ -2581,7 +2589,7 @@ dependencies = [ "hostname", "libc", "log", - "time 0.3.14", + "time", ] [[package]] @@ -2636,16 +2644,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "time" version = "0.3.14" @@ -2884,7 +2882,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.2.3", + "idna", "ipnet", "lazy_static", "log", @@ -2893,7 +2891,7 @@ dependencies = [ "thiserror", "tinyvec", "tokio", - "url 2.2.2", + "url", ] [[package]] @@ -2937,7 +2935,7 @@ dependencies = [ "rand", "sha-1", "thiserror", - "url 2.2.2", + "url", "utf-8", ] @@ -3015,17 +3013,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", -] - [[package]] name = "url" version = "2.2.2" @@ -3033,9 +3020,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" dependencies = [ "form_urlencoded", - "idna 0.2.3", + "idna", "matches", - "percent-encoding 2.1.0", + "percent-encoding", "serde", ] @@ -3094,7 +3081,7 @@ dependencies = [ "once_cell", "openssl", "paste", - "percent-encoding 2.1.0", + "percent-encoding", "pico-args", "rand", "regex", @@ -3105,12 +3092,12 @@ dependencies = [ "serde", "serde_json", "syslog", - "time 0.3.14", + "time", "tokio", "tokio-tungstenite", "totp-lite", "tracing", - "url 2.2.2", + "url", "uuid", "webauthn-rs", "yubico", @@ -3253,7 +3240,7 @@ dependencies = [ "serde_json", "thiserror", "tracing", - "url 2.2.2", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 32838c72..ce5c29d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,11 +68,11 @@ serde = { version = "1.0.144", features = ["derive"] } serde_json = "1.0.85" # A safe, extensible ORM and Query builder -diesel = { version = "1.4.8", features = ["chrono", "r2d2"] } -diesel_migrations = "1.4.0" +diesel = { version = "2.0.0", features = ["chrono", "r2d2"] } +diesel_migrations = "2.0.0" # Bundled SQLite -libsqlite3-sys = { version = "0.22.2", features = ["bundled"], optional = true } +libsqlite3-sys = { version = "0.25.1", features = ["bundled"], optional = true } # Crypto-related libraries rand = { version = "0.8.5", features = ["small_rng"] } diff --git a/src/api/admin.rs b/src/api/admin.rs index 867c8ca1..58308d8e 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -25,8 +25,6 @@ use crate::{ CONFIG, VERSION, }; -use futures::{stream, stream::StreamExt}; - pub fn routes() -> Vec { if !CONFIG.disable_admin_token() && !CONFIG.is_admin_token_set() { return routes![admin_disabled]; @@ -256,7 +254,7 @@ struct InviteData { email: String, } -async fn get_user_or_404(uuid: &str, conn: &DbConn) -> ApiResult { +async fn get_user_or_404(uuid: &str, conn: &mut DbConn) -> ApiResult { if let Some(user) = User::find_by_uuid(uuid, conn).await { Ok(user) } else { @@ -265,16 +263,16 @@ async fn get_user_or_404(uuid: &str, conn: &DbConn) -> ApiResult { } #[post("/invite", data = "")] -async fn invite_user(data: Json, _token: AdminToken, conn: DbConn) -> JsonResult { +async fn invite_user(data: Json, _token: AdminToken, mut conn: DbConn) -> JsonResult { let data: InviteData = data.into_inner(); let email = data.email.clone(); - if User::find_by_mail(&data.email, &conn).await.is_some() { + if User::find_by_mail(&data.email, &mut conn).await.is_some() { err_code!("User already exists", Status::Conflict.code) } let mut user = User::new(email); - async fn _generate_invite(user: &User, conn: &DbConn) -> EmptyResult { + async fn _generate_invite(user: &User, conn: &mut DbConn) -> EmptyResult { if CONFIG.mail_enabled() { mail::send_invite(&user.email, &user.uuid, None, None, &CONFIG.invitation_org_name(), None).await } else { @@ -283,10 +281,10 @@ async fn invite_user(data: Json, _token: AdminToken, conn: DbConn) - } } - _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))?; + _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))?; - Ok(Json(user.to_json(&conn).await)) + Ok(Json(user.to_json(&mut conn).await)) } #[post("/test/smtp", data = "")] @@ -307,93 +305,87 @@ fn logout(cookies: &CookieJar<'_>, referer: Referer) -> Redirect { } #[get("/users")] -async fn get_users_json(_token: AdminToken, conn: DbConn) -> Json { - let users_json = stream::iter(User::get_all(&conn).await) - .then(|u| async { - let u = u; // Move out this single variable - 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 - }) - .collect::>() - .await; +async fn get_users_json(_token: AdminToken, mut conn: DbConn) -> Json { + let mut users_json = Vec::new(); + for u in User::get_all(&mut conn).await { + let mut usr = u.to_json(&mut conn).await; + usr["UserEnabled"] = json!(u.enabled); + usr["CreatedAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT)); + users_json.push(usr); + } Json(Value::Array(users_json)) } #[get("/users/overview")] -async fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult> { - let users_json = stream::iter(User::get_all(&conn).await) - .then(|u| async { - let u = u; // Move out this single variable - 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(&conn).await { - Some(dt) => json!(format_naive_datetime_local(&dt, DT_FMT)), - None => json!("Never"), - }; - usr - }) - .collect::>() - .await; +async fn users_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult> { + let mut users_json = Vec::new(); + for u in User::get_all(&mut conn).await { + 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)); + 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 { + Some(dt) => json!(format_naive_datetime_local(&dt, DT_FMT)), + None => json!("Never"), + }; + users_json.push(usr); + } let text = AdminTemplateData::with_data("admin/users", json!(users_json)).render()?; Ok(Html(text)) } #[get("/users/")] -async fn get_user_json(uuid: String, _token: AdminToken, conn: DbConn) -> JsonResult { - let u = get_user_or_404(&uuid, &conn).await?; - let mut usr = u.to_json(&conn).await; +async fn get_user_json(uuid: String, _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; 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: String, _token: AdminToken, conn: DbConn) -> EmptyResult { - let user = get_user_or_404(&uuid, &conn).await?; - user.delete(&conn).await +async fn delete_user(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult { + let user = get_user_or_404(&uuid, &mut conn).await?; + user.delete(&mut conn).await } #[post("/users//deauth")] -async fn deauth_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult { - let mut user = get_user_or_404(&uuid, &conn).await?; - Device::delete_all_by_user(&user.uuid, &conn).await?; +async fn deauth_user(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult { + let mut user = get_user_or_404(&uuid, &mut conn).await?; + Device::delete_all_by_user(&user.uuid, &mut conn).await?; user.reset_security_stamp(); - user.save(&conn).await + user.save(&mut conn).await } #[post("/users//disable")] -async fn disable_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult { - let mut user = get_user_or_404(&uuid, &conn).await?; - Device::delete_all_by_user(&user.uuid, &conn).await?; +async fn disable_user(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult { + let mut user = get_user_or_404(&uuid, &mut conn).await?; + Device::delete_all_by_user(&user.uuid, &mut conn).await?; user.reset_security_stamp(); user.enabled = false; - user.save(&conn).await + user.save(&mut conn).await } #[post("/users//enable")] -async fn enable_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult { - let mut user = get_user_or_404(&uuid, &conn).await?; +async fn enable_user(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult { + let mut user = get_user_or_404(&uuid, &mut conn).await?; user.enabled = true; - user.save(&conn).await + user.save(&mut conn).await } #[post("/users//remove-2fa")] -async fn remove_2fa(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult { - let mut user = get_user_or_404(&uuid, &conn).await?; - TwoFactor::delete_all_by_user(&user.uuid, &conn).await?; +async fn remove_2fa(uuid: String, _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?; user.totp_recover = None; - user.save(&conn).await + user.save(&mut conn).await } #[derive(Deserialize, Debug)] @@ -404,13 +396,14 @@ struct UserOrgTypeData { } #[post("/users/org_type", data = "")] -async fn update_user_org_type(data: Json, _token: AdminToken, conn: DbConn) -> EmptyResult { +async fn update_user_org_type(data: Json, _token: AdminToken, mut 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, &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, &mut 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, @@ -419,7 +412,7 @@ async fn update_user_org_type(data: Json, _token: AdminToken, c if user_to_edit.atype == UserOrgType::Owner && new_type != UserOrgType::Owner { // Removing owner permmission, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(&data.org_uuid, UserOrgType::Owner, &conn).await <= 1 { + if UserOrganization::count_confirmed_by_org_and_type(&data.org_uuid, UserOrgType::Owner, &mut conn).await <= 1 { err!("Can't change the type of the last owner") } } @@ -427,7 +420,7 @@ async fn update_user_org_type(data: Json, _token: AdminToken, c // 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, &conn).await { + match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, &user_to_edit.org_uuid, true, &mut conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { err!("You cannot modify this user to this type because it has no two-step login method activated"); @@ -439,37 +432,34 @@ async fn update_user_org_type(data: Json, _token: AdminToken, c } user_to_edit.atype = new_type; - user_to_edit.save(&conn).await + user_to_edit.save(&mut conn).await } #[post("/users/update_revision")] -async fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult { - User::update_all_revisions(&conn).await +async fn update_revision_users(_token: AdminToken, mut conn: DbConn) -> EmptyResult { + User::update_all_revisions(&mut conn).await } #[get("/organizations/overview")] -async fn organizations_overview(_token: AdminToken, conn: DbConn) -> ApiResult> { - let organizations_json = stream::iter(Organization::get_all(&conn).await) - .then(|o| async { - let o = o; //Move out this single variable - let mut org = o.to_json(); - 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["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)); - org - }) - .collect::>() - .await; +async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult> { + let mut organizations_json = Vec::new(); + for o in Organization::get_all(&mut conn).await { + 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["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)); + organizations_json.push(org); + } let text = AdminTemplateData::with_data("admin/organizations", json!(organizations_json)).render()?; Ok(Html(text)) } #[post("/organizations//delete")] -async fn delete_organization(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult { - let org = Organization::find_by_uuid(&uuid, &conn).await.map_res("Organization doesn't exist")?; - org.delete(&conn).await +async fn delete_organization(uuid: String, _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 } #[derive(Deserialize)] @@ -546,7 +536,7 @@ async fn get_release_info(has_http_access: bool, running_within_docker: bool) -> } #[get("/diagnostics")] -async fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResult> { +async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn) -> ApiResult> { use chrono::prelude::*; use std::net::ToSocketAddrs; @@ -600,7 +590,7 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> A "ip_header_config": &CONFIG.ip_header(), "uses_proxy": uses_proxy, "db_type": *DB_TYPE, - "db_version": get_sql_server_version(&conn).await, + "db_version": get_sql_server_version(&mut conn).await, "admin_url": format!("{}/diagnostics", admin_url(Referer(None))), "overrides": &CONFIG.get_overrides().join(", "), "server_time_local": Local::now().format("%Y-%m-%d %H:%M:%S %Z").to_string(), @@ -629,9 +619,9 @@ fn delete_config(_token: AdminToken) -> EmptyResult { } #[post("/config/backup_db")] -async fn backup_db(_token: AdminToken, conn: DbConn) -> EmptyResult { +async fn backup_db(_token: AdminToken, mut conn: DbConn) -> EmptyResult { if *CAN_BACKUP { - backup_database(&conn).await + backup_database(&mut 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 35202698..ab75f00e 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -81,7 +81,7 @@ fn enforce_password_hint_setting(password_hint: &Option) -> EmptyResult } #[post("/accounts/register", data = "")] -async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { +async fn register(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { let data: RegisterData = data.into_inner().data; let email = data.Email.to_lowercase(); @@ -98,7 +98,7 @@ async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { let password_hint = clean_password_hint(&data.MasterPasswordHint); enforce_password_hint_setting(&password_hint)?; - let mut user = match User::find_by_mail(&email, &conn).await { + let mut user = match User::find_by_mail(&email, &mut conn).await { Some(user) => { if !user.password_hash.is_empty() { if CONFIG.is_signup_allowed(&email) { @@ -115,13 +115,13 @@ async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { } else { err!("Registration email does not match invite email") } - } else if Invitation::take(&email, &conn).await { - for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).await.iter_mut() { + } else if Invitation::take(&email, &mut conn).await { + for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &mut conn).await.iter_mut() { user_org.status = UserOrgStatus::Accepted as i32; - user_org.save(&conn).await?; + user_org.save(&mut conn).await?; } user - } else if EmergencyAccess::find_invited_by_grantee_email(&email, &conn).await.is_some() { + } else if EmergencyAccess::find_invited_by_grantee_email(&email, &mut conn).await.is_some() { user } else if CONFIG.is_signup_allowed(&email) { err!("Account with this email already exists") @@ -133,7 +133,7 @@ async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { // 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, &conn).await || CONFIG.is_signup_allowed(&email) { + if Invitation::take(&email, &mut conn).await || CONFIG.is_signup_allowed(&email) { User::new(email.clone()) } else { err!("Registration not allowed or user already exists") @@ -142,7 +142,7 @@ async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { }; // Make sure we don't leave a lingering invitation. - Invitation::take(&email, &conn).await; + Invitation::take(&email, &mut conn).await; if let Some(client_kdf_iter) = data.KdfIterations { user.client_kdf_iter = client_kdf_iter; @@ -178,12 +178,12 @@ async fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { } } - user.save(&conn).await + user.save(&mut conn).await } #[get("/accounts/profile")] -async fn profile(headers: Headers, conn: DbConn) -> Json { - Json(headers.user.to_json(&conn).await) +async fn profile(headers: Headers, mut conn: DbConn) -> Json { + Json(headers.user.to_json(&mut conn).await) } #[derive(Deserialize, Debug)] @@ -201,7 +201,7 @@ async fn put_profile(data: JsonUpcase, headers: Headers, conn: DbCo } #[post("/accounts/profile", data = "")] -async fn post_profile(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn post_profile(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: ProfileData = data.into_inner().data; // Check if the length of the username exceeds 50 characters (Same is Upstream Bitwarden) @@ -215,13 +215,13 @@ async fn post_profile(data: JsonUpcase, headers: Headers, conn: DbC user.password_hint = clean_password_hint(&data.MasterPasswordHint); enforce_password_hint_setting(&user.password_hint)?; - user.save(&conn).await?; - Ok(Json(user.to_json(&conn).await)) + user.save(&mut conn).await?; + Ok(Json(user.to_json(&mut conn).await)) } #[get("/users//public-key")] -async fn get_public_keys(uuid: String, _headers: Headers, conn: DbConn) -> JsonResult { - let user = match User::find_by_uuid(&uuid, &conn).await { +async fn get_public_keys(uuid: String, _headers: Headers, mut conn: DbConn) -> JsonResult { + let user = match User::find_by_uuid(&uuid, &mut conn).await { Some(user) => user, None => err!("User doesn't exist"), }; @@ -234,7 +234,7 @@ async fn get_public_keys(uuid: String, _headers: Headers, conn: DbConn) -> JsonR } #[post("/accounts/keys", data = "")] -async fn post_keys(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn post_keys(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: KeysData = data.into_inner().data; let mut user = headers.user; @@ -242,7 +242,7 @@ async fn post_keys(data: JsonUpcase, headers: Headers, conn: DbConn) - user.private_key = Some(data.EncryptedPrivateKey); user.public_key = Some(data.PublicKey); - user.save(&conn).await?; + user.save(&mut conn).await?; Ok(Json(json!({ "PrivateKey": user.private_key, @@ -260,7 +260,7 @@ struct ChangePassData { } #[post("/accounts/password", data = "")] -async fn post_password(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { +async fn post_password(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { let data: ChangePassData = data.into_inner().data; let mut user = headers.user; @@ -273,7 +273,7 @@ async fn post_password(data: JsonUpcase, headers: Headers, conn: Some(vec![String::from("post_rotatekey"), String::from("get_contacts"), String::from("get_public_keys")]), ); user.akey = data.Key; - user.save(&conn).await + user.save(&mut conn).await } #[derive(Deserialize)] @@ -288,7 +288,7 @@ struct ChangeKdfData { } #[post("/accounts/kdf", data = "")] -async fn post_kdf(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { +async fn post_kdf(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { let data: ChangeKdfData = data.into_inner().data; let mut user = headers.user; @@ -300,7 +300,7 @@ async fn post_kdf(data: JsonUpcase, headers: Headers, conn: DbCon user.client_kdf_type = data.Kdf; user.set_password(&data.NewMasterPasswordHash, None); user.akey = data.Key; - user.save(&conn).await + user.save(&mut conn).await } #[derive(Deserialize)] @@ -323,7 +323,7 @@ struct KeyData { } #[post("/accounts/key", data = "")] -async fn post_rotatekey(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { +async fn post_rotatekey(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { let data: KeyData = data.into_inner().data; if !headers.user.check_valid_password(&data.MasterPasswordHash) { @@ -334,7 +334,7 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, conn: DbCon // Update folder data for folder_data in data.Folders { - let mut saved_folder = match Folder::find_by_uuid(&folder_data.Id, &conn).await { + let mut saved_folder = match Folder::find_by_uuid(&folder_data.Id, &mut conn).await { Some(folder) => folder, None => err!("Folder doesn't exist"), }; @@ -344,14 +344,14 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, conn: DbCon } saved_folder.name = folder_data.Name; - saved_folder.save(&conn).await? + saved_folder.save(&mut 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(), &conn).await { + let mut saved_cipher = match Cipher::find_by_uuid(cipher_data.Id.as_ref().unwrap(), &mut conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; @@ -362,7 +362,8 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, conn: DbCon // 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. - update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None).await? + update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None) + .await? } // Update user data @@ -372,11 +373,11 @@ async fn post_rotatekey(data: JsonUpcase, headers: Headers, conn: DbCon user.private_key = Some(data.PrivateKey); user.reset_security_stamp(); - user.save(&conn).await + user.save(&mut conn).await } #[post("/accounts/security-stamp", data = "")] -async fn post_sstamp(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { +async fn post_sstamp(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { let data: PasswordData = data.into_inner().data; let mut user = headers.user; @@ -384,9 +385,9 @@ async fn post_sstamp(data: JsonUpcase, headers: Headers, conn: DbC err!("Invalid password") } - Device::delete_all_by_user(&user.uuid, &conn).await?; + Device::delete_all_by_user(&user.uuid, &mut conn).await?; user.reset_security_stamp(); - user.save(&conn).await + user.save(&mut conn).await } #[derive(Deserialize)] @@ -397,7 +398,7 @@ struct EmailTokenData { } #[post("/accounts/email-token", data = "")] -async fn post_email_token(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { +async fn post_email_token(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { let data: EmailTokenData = data.into_inner().data; let mut user = headers.user; @@ -405,7 +406,7 @@ async fn post_email_token(data: JsonUpcase, headers: Headers, co err!("Invalid password") } - if User::find_by_mail(&data.NewEmail, &conn).await.is_some() { + if User::find_by_mail(&data.NewEmail, &mut conn).await.is_some() { err!("Email already in use"); } @@ -423,7 +424,7 @@ async fn post_email_token(data: JsonUpcase, headers: Headers, co user.email_new = Some(data.NewEmail); user.email_new_token = Some(token); - user.save(&conn).await + user.save(&mut conn).await } #[derive(Deserialize)] @@ -438,7 +439,7 @@ struct ChangeEmailData { } #[post("/accounts/email", data = "")] -async fn post_email(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { +async fn post_email(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { let data: ChangeEmailData = data.into_inner().data; let mut user = headers.user; @@ -446,7 +447,7 @@ async fn post_email(data: JsonUpcase, headers: Headers, conn: D err!("Invalid password") } - if User::find_by_mail(&data.NewEmail, &conn).await.is_some() { + if User::find_by_mail(&data.NewEmail, &mut conn).await.is_some() { err!("Email already in use"); } @@ -481,7 +482,7 @@ async fn post_email(data: JsonUpcase, headers: Headers, conn: D user.set_password(&data.NewMasterPasswordHash, None); user.akey = data.Key; - user.save(&conn).await + user.save(&mut conn).await } #[post("/accounts/verify-email")] @@ -507,10 +508,10 @@ struct VerifyEmailTokenData { } #[post("/accounts/verify-email-token", data = "")] -async fn post_verify_email_token(data: JsonUpcase, conn: DbConn) -> EmptyResult { +async fn post_verify_email_token(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { let data: VerifyEmailTokenData = data.into_inner().data; - let mut user = match User::find_by_uuid(&data.UserId, &conn).await { + let mut user = match User::find_by_uuid(&data.UserId, &mut conn).await { Some(user) => user, None => err!("User doesn't exist"), }; @@ -525,7 +526,7 @@ async fn post_verify_email_token(data: JsonUpcase, conn: D user.verified_at = Some(Utc::now().naive_utc()); user.last_verifying_at = None; user.login_verify_count = 0; - if let Err(e) = user.save(&conn).await { + if let Err(e) = user.save(&mut conn).await { error!("Error saving email verification: {:#?}", e); } @@ -539,11 +540,11 @@ struct DeleteRecoverData { } #[post("/accounts/delete-recover", data = "")] -async fn post_delete_recover(data: JsonUpcase, conn: DbConn) -> EmptyResult { +async fn post_delete_recover(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { let data: DeleteRecoverData = data.into_inner().data; if CONFIG.mail_enabled() { - if let Some(user) = User::find_by_mail(&data.Email, &conn).await { + if let Some(user) = User::find_by_mail(&data.Email, &mut conn).await { if let Err(e) = mail::send_delete_account(&user.email, &user.uuid).await { error!("Error sending delete account email: {:#?}", e); } @@ -566,10 +567,10 @@ struct DeleteRecoverTokenData { } #[post("/accounts/delete-recover-token", data = "")] -async fn post_delete_recover_token(data: JsonUpcase, conn: DbConn) -> EmptyResult { +async fn post_delete_recover_token(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { let data: DeleteRecoverTokenData = data.into_inner().data; - let user = match User::find_by_uuid(&data.UserId, &conn).await { + let user = match User::find_by_uuid(&data.UserId, &mut conn).await { Some(user) => user, None => err!("User doesn't exist"), }; @@ -581,7 +582,7 @@ async fn post_delete_recover_token(data: JsonUpcase, con if claims.sub != user.uuid { err!("Invalid claim"); } - user.delete(&conn).await + user.delete(&mut conn).await } #[post("/accounts/delete", data = "")] @@ -590,7 +591,7 @@ async fn post_delete_account(data: JsonUpcase, headers: Headers, c } #[delete("/accounts", data = "")] -async fn delete_account(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { +async fn delete_account(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { let data: PasswordData = data.into_inner().data; let user = headers.user; @@ -598,7 +599,7 @@ async fn delete_account(data: JsonUpcase, headers: Headers, conn: err!("Invalid password") } - user.delete(&conn).await + user.delete(&mut conn).await } #[get("/accounts/revision-date")] @@ -614,7 +615,7 @@ struct PasswordHintData { } #[post("/accounts/password-hint", data = "")] -async fn password_hint(data: JsonUpcase, conn: DbConn) -> EmptyResult { +async fn password_hint(data: JsonUpcase, mut conn: DbConn) -> EmptyResult { if !CONFIG.mail_enabled() && !CONFIG.show_password_hint() { err!("This server is not configured to provide password hints."); } @@ -624,7 +625,7 @@ async fn password_hint(data: JsonUpcase, conn: DbConn) -> Empt let data: PasswordHintData = data.into_inner().data; let email = &data.Email; - match User::find_by_mail(email, &conn).await { + match User::find_by_mail(email, &mut conn).await { None => { // To prevent user enumeration, act as if the user exists. if CONFIG.mail_enabled() { @@ -666,10 +667,10 @@ async fn prelogin(data: JsonUpcase, conn: DbConn) -> Json { _prelogin(data, conn).await } -pub async fn _prelogin(data: JsonUpcase, conn: DbConn) -> Json { +pub async fn _prelogin(data: JsonUpcase, mut conn: DbConn) -> Json { let data: PreloginData = data.into_inner().data; - let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &conn).await { + let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &mut conn).await { Some(user) => (user.client_kdf_type, user.client_kdf_iter), None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT), }; @@ -703,7 +704,7 @@ async fn _api_key( data: JsonUpcase, rotate: bool, headers: Headers, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { let data: SecretVerificationRequest = data.into_inner().data; let mut user = headers.user; @@ -714,7 +715,7 @@ async fn _api_key( if rotate || user.api_key.is_none() { user.api_key = Some(crypto::generate_api_key()); - user.save(&conn).await.expect("Error saving API key"); + user.save(&mut conn).await.expect("Error saving API key"); } Ok(Json(json!({ diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 5256c28a..d65116a0 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -1,7 +1,6 @@ use std::collections::{HashMap, HashSet}; use chrono::{NaiveDateTime, Utc}; -use futures::{stream, stream::StreamExt}; use rocket::fs::TempFile; use rocket::serde::json::Json; use rocket::{ @@ -85,8 +84,8 @@ pub fn routes() -> Vec { pub async fn purge_trashed_ciphers(pool: DbPool) { debug!("Purging trashed ciphers"); - if let Ok(conn) = pool.get().await { - Cipher::purge_trash(&conn).await; + if let Ok(mut conn) = pool.get().await { + Cipher::purge_trash(&mut conn).await; } else { error!("Failed to get DB connection while purging trashed ciphers") } @@ -99,39 +98,33 @@ struct SyncData { } #[get("/sync?")] -async fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json { - let user_json = headers.user.to_json(&conn).await; +async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json { + let user_json = headers.user.to_json(&mut conn).await; // Get all ciphers which are visible by the user - let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn).await; + let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::User, &conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::User, &mut conn).await; // Lets generate the ciphers_json using all the gathered info - let ciphers_json: Vec = stream::iter(ciphers) - .then(|c| async { - let c = c; // Move out this single variable - c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &conn).await - }) - .collect() - .await; - - let collections_json: Vec = stream::iter(Collection::find_by_user_uuid(&headers.user.uuid, &conn).await) - .then(|c| async { - let c = c; // Move out this single variable - c.to_json_details(&headers.user.uuid, Some(&cipher_sync_data), &conn).await - }) - .collect() - .await; + let mut ciphers_json = Vec::new(); + for c in ciphers { + ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); + } + + let mut collections_json = Vec::new(); + for c in Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await { + collections_json.push(c.to_json_details(&headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); + } let folders_json: Vec = - Folder::find_by_user(&headers.user.uuid, &conn).await.iter().map(Folder::to_json).collect(); + Folder::find_by_user(&headers.user.uuid, &mut conn).await.iter().map(Folder::to_json).collect(); let sends_json: Vec = - Send::find_by_user(&headers.user.uuid, &conn).await.iter().map(Send::to_json).collect(); + Send::find_by_user(&headers.user.uuid, &mut conn).await.iter().map(Send::to_json).collect(); let policies_json: Vec = - OrgPolicy::find_confirmed_by_user(&headers.user.uuid, &conn).await.iter().map(OrgPolicy::to_json).collect(); + OrgPolicy::find_confirmed_by_user(&headers.user.uuid, &mut conn).await.iter().map(OrgPolicy::to_json).collect(); let domains_json = if data.exclude_domains { Value::Null @@ -153,17 +146,14 @@ async fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json { } #[get("/ciphers")] -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, &ciphers, CipherSyncType::User, &conn).await; - - let ciphers_json = stream::iter(ciphers) - .then(|c| async { - let c = c; // Move out this single variable - c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &conn).await - }) - .collect::>() - .await; +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, &ciphers, CipherSyncType::User, &mut conn).await; + + let mut ciphers_json = Vec::new(); + for c in ciphers { + ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); + } Json(json!({ "Data": ciphers_json, @@ -173,17 +163,17 @@ async fn get_ciphers(headers: Headers, conn: DbConn) -> Json { } #[get("/ciphers/")] -async fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { - let cipher = match Cipher::find_by_uuid(&uuid, &conn).await { +async fn get_cipher(uuid: String, headers: Headers, mut conn: DbConn) -> JsonResult { + let cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_accessible_to_user(&headers.user.uuid, &conn).await { + if !cipher.is_accessible_to_user(&headers.user.uuid, &mut conn).await { err!("Cipher is not owned by user") } - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await)) } #[get("/ciphers//admin")] @@ -269,7 +259,7 @@ async fn post_ciphers_admin( async fn post_ciphers_create( data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let mut data: ShareCipherData = data.into_inner().data; @@ -283,11 +273,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, &conn).await?; + enforce_personal_ownership_policy(Some(&data.Cipher), &headers, &mut conn).await?; let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone()); cipher.user_uuid = Some(headers.user.uuid.clone()); - cipher.save(&conn).await?; + cipher.save(&mut 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 @@ -297,12 +287,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, &conn, &nt).await + share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &nt).await } /// Called when creating a new user-owned cipher. #[post("/ciphers", data = "")] -async fn post_ciphers(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { +async fn post_ciphers(data: JsonUpcase, headers: Headers, mut 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 @@ -312,9 +302,9 @@ async fn post_ciphers(data: JsonUpcase, headers: Headers, conn: DbCo data.LastKnownRevisionDate = None; let mut cipher = Cipher::new(data.Type, data.Name.clone()); - update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherCreate).await?; + update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::CipherCreate).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await)) } /// Enforces the personal ownership policy on user-owned ciphers, if applicable. @@ -324,7 +314,11 @@ async fn post_ciphers(data: JsonUpcase, headers: Headers, conn: DbCo /// 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: &DbConn) -> EmptyResult { +async fn enforce_personal_ownership_policy( + data: Option<&CipherData>, + headers: &Headers, + conn: &mut DbConn, +) -> EmptyResult { if data.is_none() || data.unwrap().OrganizationId.is_none() { let user_uuid = &headers.user.uuid; let policy_type = OrgPolicyType::PersonalOwnership; @@ -340,7 +334,7 @@ pub async fn update_cipher_from_data( data: CipherData, headers: &Headers, shared_to_collection: bool, - conn: &DbConn, + conn: &mut DbConn, nt: &Notify<'_>, ut: UpdateType, ) -> EmptyResult { @@ -493,10 +487,10 @@ struct RelationsData { async fn post_ciphers_import( data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - enforce_personal_ownership_policy(None, &headers, &conn).await?; + enforce_personal_ownership_policy(None, &headers, &mut conn).await?; let data: ImportData = data.into_inner().data; @@ -504,7 +498,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(&conn).await?; + new_folder.save(&mut conn).await?; folders.push(new_folder); } @@ -522,11 +516,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, &conn, &nt, UpdateType::None).await?; + update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None).await?; } let mut user = headers.user; - user.update_revision(&conn).await?; + user.update_revision(&mut conn).await?; nt.send_user_update(UpdateType::Vault, &user).await; Ok(()) } @@ -570,12 +564,12 @@ async fn put_cipher( uuid: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: CipherData = data.into_inner().data; - let mut cipher = match Cipher::find_by_uuid(&uuid, &conn).await { + let mut cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; @@ -585,13 +579,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, &conn).await { + if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { err!("Cipher is not write accessible") } - update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherUpdate).await?; + update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::CipherUpdate).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await)) } #[derive(Deserialize)] @@ -635,34 +629,34 @@ async fn post_collections_admin( uuid: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { let data: CollectionsAdminData = data.into_inner().data; - let cipher = match Cipher::find_by_uuid(&uuid, &conn).await { + let cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await { + if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut 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, &conn).await.iter().cloned().collect(); + cipher.get_collections(headers.user.uuid.clone(), &mut conn).await.iter().cloned().collect(); for collection in posted_collections.symmetric_difference(¤t_collections) { - match Collection::find_by_uuid(collection, &conn).await { + match Collection::find_by_uuid(collection, &mut conn).await { None => err!("Invalid collection ID provided"), Some(collection) => { - if collection.is_writable_by_user(&headers.user.uuid, &conn).await { + if collection.is_writable_by_user(&headers.user.uuid, &mut conn).await { if posted_collections.contains(&collection.uuid) { // Add to collection - CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn).await?; + CollectionCipher::save(&cipher.uuid, &collection.uuid, &mut conn).await?; } else { // Remove from collection - CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn).await?; + CollectionCipher::delete(&cipher.uuid, &collection.uuid, &mut conn).await?; } } else { err!("No rights to modify the collection") @@ -686,12 +680,12 @@ async fn post_cipher_share( uuid: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: ShareCipherData = data.into_inner().data; - share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt).await + share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await } #[put("/ciphers//share", data = "")] @@ -699,12 +693,12 @@ async fn put_cipher_share( uuid: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: ShareCipherData = data.into_inner().data; - share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt).await + share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await } #[derive(Deserialize)] @@ -718,7 +712,7 @@ struct ShareSelectedCipherData { async fn put_cipher_share_selected( data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let mut data: ShareSelectedCipherData = data.into_inner().data; @@ -746,7 +740,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, &conn, &nt).await?, + Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &nt).await?, None => err!("Request missing ids field"), }; } @@ -758,7 +752,7 @@ async fn share_cipher_by_uuid( uuid: &str, data: ShareCipherData, headers: &Headers, - conn: &DbConn, + conn: &mut DbConn, nt: &Notify<'_>, ) -> JsonResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { @@ -816,8 +810,8 @@ 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: String, attachment_id: String, headers: Headers, conn: DbConn) -> JsonResult { - match Attachment::find_by_id(&attachment_id, &conn).await { +async fn get_attachment(uuid: String, attachment_id: String, headers: Headers, mut conn: DbConn) -> JsonResult { + match Attachment::find_by_id(&attachment_id, &mut 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"), @@ -847,14 +841,14 @@ async fn post_attachment_v2( uuid: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { - let cipher = match Cipher::find_by_uuid(&uuid, &conn).await { + let cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await { + if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { err!("Cipher is not write accessible") } @@ -862,7 +856,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(&conn).await.expect("Error saving attachment"); + attachment.save(&mut conn).await.expect("Error saving attachment"); let url = format!("/ciphers/{}/attachment/{}", cipher.uuid, attachment_id); let response_key = match data.AdminRequest { @@ -875,7 +869,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, &conn).await, + response_key: cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await, }))) } @@ -898,15 +892,15 @@ async fn save_attachment( cipher_uuid: String, data: Form>, headers: &Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> Result<(Cipher, DbConn), crate::error::Error> { - let cipher = match Cipher::find_by_uuid(&cipher_uuid, &conn).await { + let cipher = match Cipher::find_by_uuid(&cipher_uuid, &mut conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn).await { + if !cipher.is_write_accessible_to_user(&headers.user.uuid, &mut conn).await { err!("Cipher is not write accessible") } @@ -921,7 +915,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, &conn).await + size_adjust; + let left = (limit_kb * 1024) - Attachment::size_by_user(user_uuid, &mut conn).await + size_adjust; if left <= 0 { err!("Attachment storage limit reached! Delete some attachments to free up space") } @@ -933,7 +927,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, &conn).await + size_adjust; + let left = (limit_kb * 1024) - Attachment::size_by_org(org_uuid, &mut conn).await + size_adjust; if left <= 0 { err!("Attachment storage limit reached! Delete some attachments to free up space") } @@ -988,10 +982,10 @@ async fn save_attachment( if size != attachment.file_size { // Update the attachment with the actual file size. attachment.file_size = size; - attachment.save(&conn).await.expect("Error updating attachment"); + attachment.save(&mut conn).await.expect("Error updating attachment"); } } else { - attachment.delete(&conn).await.ok(); + attachment.delete(&mut conn).await.ok(); err!(format!("Attachment size mismatch (expected within [{}, {}], got {})", min_size, max_size, size)); } @@ -1006,14 +1000,14 @@ async fn save_attachment( err!("No attachment key provided") } let attachment = Attachment::new(file_id, cipher_uuid.clone(), encrypted_filename.unwrap(), size, data.key); - attachment.save(&conn).await.expect("Error saving attachment"); + attachment.save(&mut conn).await.expect("Error saving attachment"); } if let Err(_err) = data.data.persist_to(&file_path).await { data.data.move_copy_to(file_path).await? } - nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn).await).await; + nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&mut conn).await).await; Ok((cipher, conn)) } @@ -1028,10 +1022,10 @@ async fn post_attachment_v2_data( attachment_id: String, data: Form>, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - let attachment = match Attachment::find_by_id(&attachment_id, &conn).await { + let attachment = match Attachment::find_by_id(&attachment_id, &mut 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"), @@ -1055,9 +1049,9 @@ async fn post_attachment( // the attachment database record as well as saving the data to disk. let attachment = None; - let (cipher, conn) = save_attachment(attachment, uuid, data, &headers, conn, nt).await?; + let (cipher, mut conn) = save_attachment(attachment, uuid, data, &headers, conn, nt).await?; - Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &conn).await)) + Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await)) } #[post("/ciphers//attachment-admin", format = "multipart/form-data", data = "")] @@ -1077,10 +1071,10 @@ async fn post_attachment_share( attachment_id: String, data: Form>, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt).await?; + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await?; post_attachment(uuid, data, headers, conn, nt).await } @@ -1111,10 +1105,10 @@ async fn delete_attachment( uuid: String, attachment_id: String, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt).await + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await } #[delete("/ciphers//attachment//admin")] @@ -1122,40 +1116,40 @@ async fn delete_attachment_admin( uuid: String, attachment_id: String, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt).await + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await } #[post("/ciphers//delete")] -async fn delete_cipher_post(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt).await +async fn delete_cipher_post(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await } #[post("/ciphers//delete-admin")] -async fn delete_cipher_post_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt).await +async fn delete_cipher_post_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await } #[put("/ciphers//delete")] -async fn delete_cipher_put(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &conn, true, &nt).await +async fn delete_cipher_put(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await } #[put("/ciphers//delete-admin")] -async fn delete_cipher_put_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &conn, true, &nt).await +async fn delete_cipher_put_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &nt).await } #[delete("/ciphers/")] -async fn delete_cipher(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt).await +async fn delete_cipher(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await } #[delete("/ciphers//admin")] -async fn delete_cipher_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &conn, false, &nt).await +async fn delete_cipher_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { + _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &nt).await } #[delete("/ciphers", data = "")] @@ -1219,23 +1213,23 @@ async fn delete_cipher_selected_put_admin( } #[put("/ciphers//restore")] -async fn restore_cipher_put(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { - _restore_cipher_by_uuid(&uuid, &headers, &conn, &nt).await +async fn restore_cipher_put(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { + _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await } #[put("/ciphers//restore-admin")] -async fn restore_cipher_put_admin(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { - _restore_cipher_by_uuid(&uuid, &headers, &conn, &nt).await +async fn restore_cipher_put_admin(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { + _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &nt).await } #[put("/ciphers/restore", data = "")] async fn restore_cipher_selected( data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { - _restore_multiple_ciphers(data, &headers, &conn, &nt).await + _restore_multiple_ciphers(data, &headers, &mut conn, &nt).await } #[derive(Deserialize)] @@ -1249,14 +1243,14 @@ struct MoveCipherData { async fn move_cipher_selected( data: JsonUpcase, headers: Headers, - conn: DbConn, + mut 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, &conn).await { + match Folder::find_by_uuid(folder_id, &mut conn).await { Some(folder) => { if folder.user_uuid != user_uuid { err!("Folder is not owned by user") @@ -1267,17 +1261,17 @@ async fn move_cipher_selected( } for uuid in data.Ids { - let cipher = match Cipher::find_by_uuid(&uuid, &conn).await { + let cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if !cipher.is_accessible_to_user(&user_uuid, &conn).await { + if !cipher.is_accessible_to_user(&user_uuid, &mut conn).await { err!("Cipher is not accessible by user") } // Move cipher - cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &conn).await?; + cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &mut conn).await?; nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &[user_uuid.clone()]).await; } @@ -1306,7 +1300,7 @@ async fn delete_all( organization: Option, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let data: PasswordData = data.into_inner().data; @@ -1321,11 +1315,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, &conn).await { + match UserOrganization::find_by_user_and_org(&user.uuid, &org_data.org_id, &mut 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, &conn).await?; + Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?; nt.send_user_update(UpdateType::Vault, &user).await; Ok(()) } else { @@ -1337,16 +1331,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, &conn).await { - cipher.delete(&conn).await?; + for cipher in Cipher::find_owned_by_user(&user.uuid, &mut conn).await { + cipher.delete(&mut conn).await?; } // Delete folders - for f in Folder::find_by_user(&user.uuid, &conn).await { - f.delete(&conn).await?; + for f in Folder::find_by_user(&user.uuid, &mut conn).await { + f.delete(&mut conn).await?; } - user.update_revision(&conn).await?; + user.update_revision(&mut conn).await?; nt.send_user_update(UpdateType::Vault, &user).await; Ok(()) } @@ -1356,7 +1350,7 @@ async fn delete_all( async fn _delete_cipher_by_uuid( uuid: &str, headers: &Headers, - conn: &DbConn, + conn: &mut DbConn, soft_delete: bool, nt: &Notify<'_>, ) -> EmptyResult { @@ -1384,7 +1378,7 @@ async fn _delete_cipher_by_uuid( async fn _delete_multiple_ciphers( data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, soft_delete: bool, nt: Notify<'_>, ) -> EmptyResult { @@ -1399,7 +1393,7 @@ async fn _delete_multiple_ciphers( }; for uuid in uuids { - if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &conn, soft_delete, &nt).await { + if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &mut conn, soft_delete, &nt).await { return error; }; } @@ -1407,7 +1401,7 @@ async fn _delete_multiple_ciphers( Ok(()) } -async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, nt: &Notify<'_>) -> JsonResult { +async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &mut DbConn, nt: &Notify<'_>) -> JsonResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), @@ -1427,7 +1421,7 @@ async fn _restore_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, n async fn _restore_multiple_ciphers( data: JsonUpcase, headers: &Headers, - conn: &DbConn, + conn: &mut DbConn, nt: &Notify<'_>, ) -> JsonResult { let data: Value = data.into_inner().data; @@ -1459,7 +1453,7 @@ async fn _delete_cipher_attachment_by_id( uuid: &str, attachment_id: &str, headers: &Headers, - conn: &DbConn, + conn: &mut DbConn, nt: &Notify<'_>, ) -> EmptyResult { let attachment = match Attachment::find_by_id(attachment_id, conn).await { @@ -1505,9 +1499,9 @@ pub enum CipherSyncType { } impl CipherSyncData { - pub async fn new(user_uuid: &str, ciphers: &Vec, sync_type: CipherSyncType, conn: &DbConn) -> Self { + pub async fn new(user_uuid: &str, ciphers: &[Cipher], sync_type: CipherSyncType, conn: &mut DbConn) -> Self { // Generate a list of Cipher UUID's to be used during a query filter with an eq_any. - let cipher_uuids = stream::iter(ciphers).map(|c| c.uuid.clone()).collect::>().await; + let cipher_uuids = ciphers.iter().map(|c| c.uuid.clone()).collect(); let mut cipher_folders: HashMap = HashMap::new(); let mut cipher_favorites: HashSet = HashSet::new(); @@ -1515,11 +1509,10 @@ impl CipherSyncData { // User Sync supports Folders and Favorits CipherSyncType::User => { // Generate a HashMap with the Cipher UUID as key and the Folder UUID as value - cipher_folders = stream::iter(FolderCipher::find_by_user(user_uuid, conn).await).collect().await; + cipher_folders = FolderCipher::find_by_user(user_uuid, conn).await.into_iter().collect(); // Generate a HashSet of all the Cipher UUID's which are marked as favorite - cipher_favorites = - stream::iter(Favorite::get_all_cipher_uuid_by_user(user_uuid, conn).await).collect().await; + cipher_favorites = Favorite::get_all_cipher_uuid_by_user(user_uuid, conn).await.into_iter().collect(); } // Organization Sync does not support Folders and Favorits. // If these are set, it will cause issues in the web-vault. @@ -1534,23 +1527,23 @@ impl CipherSyncData { // Generate a HashMap with the Cipher UUID as key and one or more Collection UUID's let mut cipher_collections: HashMap> = HashMap::new(); - for (cipher, collection) in Cipher::get_collections_with_cipher_by_user(user_uuid, conn).await { + for (cipher, collection) in Cipher::get_collections_with_cipher_by_user(user_uuid.to_string(), conn).await { cipher_collections.entry(cipher).or_default().push(collection); } // Generate a HashMap with the Organization UUID as key and the UserOrganization record - let user_organizations: HashMap = - stream::iter(UserOrganization::find_by_user(user_uuid, conn).await) - .map(|uo| (uo.org_uuid.clone(), uo)) - .collect() - .await; + let user_organizations: HashMap = UserOrganization::find_by_user(user_uuid, conn) + .await + .into_iter() + .map(|uo| (uo.org_uuid.clone(), uo)) + .collect(); // Generate a HashMap with the User_Collections UUID as key and the CollectionUser record - let user_collections: HashMap = - stream::iter(CollectionUser::find_by_user(user_uuid, conn).await) - .map(|uc| (uc.collection_uuid.clone(), uc)) - .collect() - .await; + let user_collections: HashMap = CollectionUser::find_by_user(user_uuid, conn) + .await + .into_iter() + .map(|uc| (uc.collection_uuid.clone(), uc)) + .collect(); Self { cipher_attachments, diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs index 5ca86910..7c3b09c5 100644 --- a/src/api/core/emergency_access.rs +++ b/src/api/core/emergency_access.rs @@ -2,7 +2,6 @@ use chrono::{Duration, Utc}; use rocket::serde::json::Json; use rocket::Route; use serde_json::Value; -use std::borrow::Borrow; use crate::{ api::{ @@ -14,8 +13,6 @@ use crate::{ mail, CONFIG, }; -use futures::{stream, stream::StreamExt}; - pub fn routes() -> Vec { routes![ get_contacts, @@ -41,17 +38,13 @@ pub fn routes() -> Vec { // region get #[get("/emergency-access/trusted")] -async fn get_contacts(headers: Headers, conn: DbConn) -> JsonResult { +async fn get_contacts(headers: Headers, mut conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - let emergency_access_list_json = - stream::iter(EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &conn).await) - .then(|e| async { - let e = e; // Move out this single variable - e.to_json_grantee_details(&conn).await - }) - .collect::>() - .await; + let mut emergency_access_list_json = Vec::new(); + for e in EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &mut conn).await { + emergency_access_list_json.push(e.to_json_grantee_details(&mut conn).await); + } Ok(Json(json!({ "Data": emergency_access_list_json, @@ -61,17 +54,13 @@ async fn get_contacts(headers: Headers, conn: DbConn) -> JsonResult { } #[get("/emergency-access/granted")] -async fn get_grantees(headers: Headers, conn: DbConn) -> JsonResult { +async fn get_grantees(headers: Headers, mut conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - let emergency_access_list_json = - stream::iter(EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &conn).await) - .then(|e| async { - let e = e; // Move out this single variable - e.to_json_grantor_details(&conn).await - }) - .collect::>() - .await; + let mut emergency_access_list_json = Vec::new(); + for e in EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &mut conn).await { + emergency_access_list_json.push(e.to_json_grantor_details(&mut conn).await); + } Ok(Json(json!({ "Data": emergency_access_list_json, @@ -81,11 +70,11 @@ async fn get_grantees(headers: Headers, conn: DbConn) -> JsonResult { } #[get("/emergency-access/")] -async fn get_emergency_access(emer_id: String, conn: DbConn) -> JsonResult { +async fn get_emergency_access(emer_id: String, mut conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; - match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { - Some(emergency_access) => Ok(Json(emergency_access.to_json_grantee_details(&conn).await)), + match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { + Some(emergency_access) => Ok(Json(emergency_access.to_json_grantee_details(&mut conn).await)), None => err!("Emergency access not valid."), } } @@ -115,13 +104,13 @@ async fn put_emergency_access( async fn post_emergency_access( emer_id: String, data: JsonUpcase, - conn: DbConn, + mut 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, &conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emergency_access) => emergency_access, None => err!("Emergency access not valid."), }; @@ -135,7 +124,7 @@ async fn post_emergency_access( emergency_access.wait_time_days = data.WaitTimeDays; emergency_access.key_encrypted = data.KeyEncrypted; - emergency_access.save(&conn).await?; + emergency_access.save(&mut conn).await?; Ok(Json(emergency_access.to_json())) } @@ -144,12 +133,12 @@ async fn post_emergency_access( // region delete #[delete("/emergency-access/")] -async fn delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult { +async fn delete_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> EmptyResult { check_emergency_access_allowed()?; let grantor_user = headers.user; - let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => { if emer.grantor_uuid != grantor_user.uuid && emer.grantee_uuid != Some(grantor_user.uuid) { err!("Emergency access not valid.") @@ -158,7 +147,7 @@ async fn delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn } None => err!("Emergency access not valid."), }; - emergency_access.delete(&conn).await?; + emergency_access.delete(&mut conn).await?; Ok(()) } @@ -180,7 +169,7 @@ struct EmergencyAccessInviteData { } #[post("/emergency-access/invite", data = "")] -async fn send_invite(data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { +async fn send_invite(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { check_emergency_access_allowed()?; let data: EmergencyAccessInviteData = data.into_inner().data; @@ -201,7 +190,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, &conn).await { + let grantee_user = match User::find_by_mail(&email, &mut conn).await { None => { if !CONFIG.invitations_allowed() { err!(format!("Grantee user does not exist: {}", email)) @@ -213,11 +202,11 @@ async fn send_invite(data: JsonUpcase, headers: Heade if !CONFIG.mail_enabled() { let invitation = Invitation::new(email.clone()); - invitation.save(&conn).await?; + invitation.save(&mut conn).await?; } let mut user = User::new(email.clone()); - user.save(&conn).await?; + user.save(&mut conn).await?; user } Some(user) => user, @@ -227,7 +216,7 @@ async fn send_invite(data: JsonUpcase, headers: Heade &grantor_user.uuid, &grantee_user.uuid, &grantee_user.email, - &conn, + &mut conn, ) .await .is_some() @@ -242,7 +231,7 @@ async fn send_invite(data: JsonUpcase, headers: Heade new_type, wait_time_days, ); - new_emergency_access.save(&conn).await?; + new_emergency_access.save(&mut conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_invite( @@ -255,9 +244,9 @@ 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, &conn).await { + match User::find_by_mail(&email, &mut conn).await { Some(user) => { - match accept_invite_process(user.uuid, new_emergency_access.uuid, Some(email), conn.borrow()).await { + match accept_invite_process(user.uuid, new_emergency_access.uuid, Some(email), &mut conn).await { Ok(v) => v, Err(e) => err!(e.to_string()), } @@ -270,10 +259,10 @@ async fn send_invite(data: JsonUpcase, headers: Heade } #[post("/emergency-access//reinvite")] -async fn resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult { +async fn resend_invite(emer_id: String, headers: Headers, mut conn: DbConn) -> EmptyResult { check_emergency_access_allowed()?; - let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -291,7 +280,7 @@ async fn resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> Empty None => err!("Email not valid."), }; - let grantee_user = match User::find_by_mail(&email, &conn).await { + let grantee_user = match User::find_by_mail(&email, &mut conn).await { Some(user) => user, None => err!("Grantee user not found."), }; @@ -308,15 +297,13 @@ async fn resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> Empty ) .await?; } else { - if Invitation::find_by_mail(&email, &conn).await.is_none() { + if Invitation::find_by_mail(&email, &mut conn).await.is_none() { let invitation = Invitation::new(email); - invitation.save(&conn).await?; + invitation.save(&mut conn).await?; } // Automatically mark user as accepted if no email invites - match accept_invite_process(grantee_user.uuid, emergency_access.uuid, emergency_access.email, conn.borrow()) - .await - { + match accept_invite_process(grantee_user.uuid, emergency_access.uuid, emergency_access.email, &mut conn).await { Ok(v) => v, Err(e) => err!(e.to_string()), } @@ -332,28 +319,28 @@ struct AcceptData { } #[post("/emergency-access//accept", data = "")] -async fn accept_invite(emer_id: String, data: JsonUpcase, conn: DbConn) -> EmptyResult { +async fn accept_invite(emer_id: String, data: JsonUpcase, mut conn: DbConn) -> EmptyResult { check_emergency_access_allowed()?; let data: AcceptData = data.into_inner().data; let token = &data.Token; let claims = decode_emergency_access_invite(token)?; - let grantee_user = match User::find_by_mail(&claims.email, &conn).await { + let grantee_user = match User::find_by_mail(&claims.email, &mut conn).await { Some(user) => { - Invitation::take(&claims.email, &conn).await; + Invitation::take(&claims.email, &mut conn).await; user } None => err!("Invited user not found"), }; - let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut 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, &conn).await { + let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { Some(user) => user, None => err!("Grantor user not found."), }; @@ -362,7 +349,9 @@ async fn accept_invite(emer_id: String, data: JsonUpcase, conn: DbCo && (claims.grantor_name.is_some() && grantor_user.name == claims.grantor_name.unwrap()) && (claims.grantor_email.is_some() && grantor_user.email == claims.grantor_email.unwrap()) { - match accept_invite_process(grantee_user.uuid.clone(), emer_id, Some(grantee_user.email.clone()), &conn).await { + match accept_invite_process(grantee_user.uuid.clone(), emer_id, Some(grantee_user.email.clone()), &mut conn) + .await + { Ok(v) => v, Err(e) => err!(e.to_string()), } @@ -381,7 +370,7 @@ async fn accept_invite_process( grantee_uuid: String, emer_id: String, email: Option, - conn: &DbConn, + conn: &mut DbConn, ) -> EmptyResult { let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, conn).await { Some(emer) => emer, @@ -414,7 +403,7 @@ async fn confirm_emergency_access( emer_id: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { check_emergency_access_allowed()?; @@ -422,7 +411,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, &conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -433,13 +422,13 @@ async fn confirm_emergency_access( err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&confirming_user.uuid, &conn).await { + let grantor_user = match User::find_by_uuid(&confirming_user.uuid, &mut 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, &conn).await { + let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { Some(user) => user, None => err!("Grantee user not found."), }; @@ -448,7 +437,7 @@ async fn confirm_emergency_access( emergency_access.key_encrypted = Some(key); emergency_access.email = None; - emergency_access.save(&conn).await?; + emergency_access.save(&mut conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_invite_confirmed(&grantee_user.email, &grantor_user.name).await?; @@ -464,11 +453,11 @@ async fn confirm_emergency_access( // region access emergency access #[post("/emergency-access//initiate")] -async fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult { +async fn initiate_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; let initiating_user = headers.user; - let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -479,7 +468,7 @@ async fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbCo err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { + let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { Some(user) => user, None => err!("Grantor user not found."), }; @@ -489,7 +478,7 @@ async fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbCo emergency_access.updated_at = now; emergency_access.recovery_initiated_at = Some(now); emergency_access.last_notification_at = Some(now); - emergency_access.save(&conn).await?; + emergency_access.save(&mut conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_recovery_initiated( @@ -504,11 +493,11 @@ async fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbCo } #[post("/emergency-access//approve")] -async fn approve_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult { +async fn approve_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; let approving_user = headers.user; - let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -519,19 +508,19 @@ async fn approve_emergency_access(emer_id: String, headers: Headers, conn: DbCon err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&approving_user.uuid, &conn).await { + let grantor_user = match User::find_by_uuid(&approving_user.uuid, &mut 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, &conn).await { + let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { Some(user) => user, None => err!("Grantee user not found."), }; emergency_access.status = EmergencyAccessStatus::RecoveryApproved as i32; - emergency_access.save(&conn).await?; + emergency_access.save(&mut conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_recovery_approved(&grantee_user.email, &grantor_user.name).await?; @@ -543,11 +532,11 @@ async fn approve_emergency_access(emer_id: String, headers: Headers, conn: DbCon } #[post("/emergency-access//reject")] -async fn reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult { +async fn reject_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; let rejecting_user = headers.user; - let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -559,19 +548,19 @@ async fn reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&rejecting_user.uuid, &conn).await { + let grantor_user = match User::find_by_uuid(&rejecting_user.uuid, &mut 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, &conn).await { + let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await { Some(user) => user, None => err!("Grantee user not found."), }; emergency_access.status = EmergencyAccessStatus::Confirmed as i32; - emergency_access.save(&conn).await?; + emergency_access.save(&mut conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_recovery_rejected(&grantee_user.email, &grantor_user.name).await?; @@ -587,12 +576,12 @@ async fn reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn // region action #[post("/emergency-access//view")] -async fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult { +async fn view_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; let requesting_user = headers.user; let host = headers.host; - let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -601,17 +590,14 @@ async fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) err!("Emergency access not valid.") } - let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &conn).await; + let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &mut conn).await; let cipher_sync_data = - CipherSyncData::new(&emergency_access.grantor_uuid, &ciphers, CipherSyncType::User, &conn).await; + CipherSyncData::new(&emergency_access.grantor_uuid, &ciphers, CipherSyncType::User, &mut conn).await; - let ciphers_json = stream::iter(ciphers) - .then(|c| async { - let c = c; // Move out this single variable - c.to_json(&host, &emergency_access.grantor_uuid, Some(&cipher_sync_data), &conn).await - }) - .collect::>() - .await; + let mut ciphers_json = Vec::new(); + for c in ciphers { + ciphers_json.push(c.to_json(&host, &emergency_access.grantor_uuid, Some(&cipher_sync_data), &mut conn).await); + } Ok(Json(json!({ "Ciphers": ciphers_json, @@ -621,11 +607,11 @@ async fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) } #[post("/emergency-access//takeover")] -async fn takeover_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult { +async fn takeover_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult { check_emergency_access_allowed()?; let requesting_user = headers.user; - let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -634,7 +620,7 @@ async fn takeover_emergency_access(emer_id: String, headers: Headers, conn: DbCo err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { + let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { Some(user) => user, None => err!("Grantor user not found."), }; @@ -659,7 +645,7 @@ async fn password_emergency_access( emer_id: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { check_emergency_access_allowed()?; @@ -668,7 +654,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, &conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -677,7 +663,7 @@ async fn password_emergency_access( err!("Emergency access not valid.") } - let mut grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { + let mut grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { Some(user) => user, None => err!("Grantor user not found."), }; @@ -685,15 +671,15 @@ async fn password_emergency_access( // change grantor_user password grantor_user.set_password(new_master_password_hash, None); grantor_user.akey = key; - grantor_user.save(&conn).await?; + grantor_user.save(&mut conn).await?; // Disable TwoFactor providers since they will otherwise block logins - TwoFactor::delete_all_by_user(&grantor_user.uuid, &conn).await?; + TwoFactor::delete_all_by_user(&grantor_user.uuid, &mut conn).await?; // Remove grantor from all organisations unless Owner - for user_org in UserOrganization::find_any_state_by_user(&grantor_user.uuid, &conn).await { + for user_org in UserOrganization::find_any_state_by_user(&grantor_user.uuid, &mut conn).await { if user_org.atype != UserOrgType::Owner as i32 { - user_org.delete(&conn).await?; + user_org.delete(&mut conn).await?; } } Ok(()) @@ -702,9 +688,9 @@ async fn password_emergency_access( // endregion #[get("/emergency-access//policies")] -async fn policies_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult { +async fn policies_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult { let requesting_user = headers.user; - let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn).await { + let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await { Some(emer) => emer, None => err!("Emergency access not valid."), }; @@ -713,12 +699,12 @@ async fn policies_emergency_access(emer_id: String, headers: Headers, conn: DbCo err!("Emergency access not valid.") } - let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn).await { + let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await { Some(user) => user, None => err!("Grantor user not found."), }; - let policies = OrgPolicy::find_confirmed_by_user(&grantor_user.uuid, &conn); + let policies = OrgPolicy::find_confirmed_by_user(&grantor_user.uuid, &mut conn); let policies_json: Vec = policies.await.iter().map(OrgPolicy::to_json).collect(); Ok(Json(json!({ @@ -751,8 +737,8 @@ pub async fn emergency_request_timeout_job(pool: DbPool) { return; } - if let Ok(conn) = pool.get().await { - let emergency_access_list = EmergencyAccess::find_all_recoveries(&conn).await; + if let Ok(mut conn) = pool.get().await { + let emergency_access_list = EmergencyAccess::find_all_recoveries(&mut conn).await; if emergency_access_list.is_empty() { debug!("No emergency request timeout to approve"); @@ -764,16 +750,16 @@ pub async fn emergency_request_timeout_job(pool: DbPool) { >= emer.recovery_initiated_at.unwrap() + Duration::days(i64::from(emer.wait_time_days)) { emer.status = EmergencyAccessStatus::RecoveryApproved as i32; - emer.save(&conn).await.expect("Cannot save emergency access on job"); + emer.save(&mut conn).await.expect("Cannot save emergency access on job"); if CONFIG.mail_enabled() { // get grantor user to send Accepted email let grantor_user = - User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found."); + User::find_by_uuid(&emer.grantor_uuid, &mut 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."), &conn) + User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid."), &mut conn) .await .expect("Grantee user not found."); @@ -802,8 +788,8 @@ pub async fn emergency_notification_reminder_job(pool: DbPool) { return; } - if let Ok(conn) = pool.get().await { - let emergency_access_list = EmergencyAccess::find_all_recoveries(&conn).await; + if let Ok(mut conn) = pool.get().await { + let emergency_access_list = EmergencyAccess::find_all_recoveries(&mut conn).await; if emergency_access_list.is_empty() { debug!("No emergency request reminder notification to send"); @@ -817,16 +803,16 @@ pub async fn emergency_notification_reminder_job(pool: DbPool) { || (emer.last_notification_at.is_some() && Utc::now().naive_utc() >= emer.last_notification_at.unwrap() + Duration::days(1))) { - emer.save(&conn).await.expect("Cannot save emergency access on job"); + emer.save(&mut conn).await.expect("Cannot save emergency access on job"); if CONFIG.mail_enabled() { // get grantor user to send Accepted email let grantor_user = - User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found."); + User::find_by_uuid(&emer.grantor_uuid, &mut 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."), &conn) + User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid."), &mut conn) .await .expect("Grantee user not found."); diff --git a/src/api/core/folders.rs b/src/api/core/folders.rs index a5997983..95155803 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, conn: DbConn) -> Json { - let folders = Folder::find_by_user(&headers.user.uuid, &conn).await; +async fn get_folders(headers: Headers, mut conn: DbConn) -> Json { + let folders = Folder::find_by_user(&headers.user.uuid, &mut 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, conn: DbConn) -> Json { } #[get("/folders/")] -async fn get_folder(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { - let folder = match Folder::find_by_uuid(&uuid, &conn).await { +async fn get_folder(uuid: String, headers: Headers, mut conn: DbConn) -> JsonResult { + let folder = match Folder::find_by_uuid(&uuid, &mut conn).await { Some(folder) => folder, _ => err!("Invalid folder"), }; @@ -44,12 +44,12 @@ pub struct FolderData { } #[post("/folders", data = "")] -async fn post_folders(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { +async fn post_folders(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { let data: FolderData = data.into_inner().data; let mut folder = Folder::new(headers.user.uuid, data.Name); - folder.save(&conn).await?; + folder.save(&mut conn).await?; nt.send_folder_update(UpdateType::FolderCreate, &folder).await; Ok(Json(folder.to_json())) @@ -71,12 +71,12 @@ async fn put_folder( uuid: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { let data: FolderData = data.into_inner().data; - let mut folder = match Folder::find_by_uuid(&uuid, &conn).await { + let mut folder = match Folder::find_by_uuid(&uuid, &mut conn).await { Some(folder) => folder, _ => err!("Invalid folder"), }; @@ -87,7 +87,7 @@ async fn put_folder( folder.name = data.Name; - folder.save(&conn).await?; + folder.save(&mut conn).await?; nt.send_folder_update(UpdateType::FolderUpdate, &folder).await; Ok(Json(folder.to_json())) @@ -99,8 +99,8 @@ async fn delete_folder_post(uuid: String, headers: Headers, conn: DbConn, nt: No } #[delete("/folders/")] -async fn delete_folder(uuid: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { - let folder = match Folder::find_by_uuid(&uuid, &conn).await { +async fn delete_folder(uuid: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { + let folder = match Folder::find_by_uuid(&uuid, &mut conn).await { Some(folder) => folder, _ => err!("Invalid folder"), }; @@ -110,7 +110,7 @@ async fn delete_folder(uuid: String, headers: Headers, conn: DbConn, nt: Notify< } // Delete the actual folder entry - folder.delete(&conn).await?; + folder.delete(&mut conn).await?; nt.send_folder_update(UpdateType::FolderDelete, &folder).await; Ok(()) diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index c54ebeb7..79a6edb9 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -127,7 +127,7 @@ struct EquivDomainData { } #[post("/settings/domains", data = "")] -async fn post_eq_domains(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn post_eq_domains(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EquivDomainData = data.into_inner().data; let excluded_globals = data.ExcludedGlobalEquivalentDomains.unwrap_or_default(); @@ -139,7 +139,7 @@ async fn post_eq_domains(data: JsonUpcase, headers: Headers, co 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(&conn).await?; + user.save(&mut conn).await?; Ok(Json(json!({}))) } diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 9b7d264c..fcd954c1 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -10,11 +10,10 @@ use crate::{ }, auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, db::{models::*, DbConn}, + error::Error, mail, CONFIG, }; -use futures::{stream, stream::StreamExt}; - pub fn routes() -> Vec { routes![ get_organization, @@ -107,11 +106,11 @@ struct OrgBulkIds { } #[post("/organizations", data = "")] -async fn create_organization(headers: Headers, data: JsonUpcase, conn: DbConn) -> JsonResult { +async fn create_organization(headers: Headers, data: JsonUpcase, mut 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, &conn).await { + if OrgPolicy::is_applicable_to_user(&headers.user.uuid, OrgPolicyType::SingleOrg, None, &mut 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." ) @@ -134,9 +133,9 @@ async fn create_organization(headers: Headers, data: JsonUpcase, conn: user_org.atype = UserOrgType::Owner as i32; user_org.status = UserOrgStatus::Confirmed as i32; - org.save(&conn).await?; - user_org.save(&conn).await?; - collection.save(&conn).await?; + org.save(&mut conn).await?; + user_org.save(&mut conn).await?; + collection.save(&mut conn).await?; Ok(Json(org.to_json())) } @@ -146,7 +145,7 @@ async fn delete_organization( org_id: String, data: JsonUpcase, headers: OwnerHeaders, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { let data: PasswordData = data.into_inner().data; let password_hash = data.MasterPasswordHash; @@ -155,9 +154,9 @@ async fn delete_organization( err!("Invalid password") } - match Organization::find_by_uuid(&org_id, &conn).await { + match Organization::find_by_uuid(&org_id, &mut conn).await { None => err!("Organization not found"), - Some(org) => org.delete(&conn).await, + Some(org) => org.delete(&mut conn).await, } } @@ -172,24 +171,24 @@ async fn post_delete_organization( } #[post("/organizations//leave")] -async fn leave_organization(org_id: String, headers: Headers, conn: DbConn) -> EmptyResult { - match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await { +async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn) -> EmptyResult { + match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut 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, &conn).await <= 1 + && UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &mut conn).await <= 1 { err!("The last owner can't leave") } - user_org.delete(&conn).await + user_org.delete(&mut conn).await } } } #[get("/organizations/")] -async fn get_organization(org_id: String, _headers: OwnerHeaders, conn: DbConn) -> JsonResult { - match Organization::find_by_uuid(&org_id, &conn).await { +async fn get_organization(org_id: String, _headers: OwnerHeaders, mut conn: DbConn) -> JsonResult { + match Organization::find_by_uuid(&org_id, &mut conn).await { Some(organization) => Ok(Json(organization.to_json())), None => err!("Can't find organization details"), } @@ -210,11 +209,11 @@ async fn post_organization( org_id: String, _headers: OwnerHeaders, data: JsonUpcase, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { let data: OrganizationUpdateData = data.into_inner().data; - let mut org = match Organization::find_by_uuid(&org_id, &conn).await { + let mut org = match Organization::find_by_uuid(&org_id, &mut conn).await { Some(organization) => organization, None => err!("Can't find organization details"), }; @@ -222,16 +221,16 @@ async fn post_organization( org.name = data.Name; org.billing_email = data.BillingEmail; - org.save(&conn).await?; + org.save(&mut conn).await?; Ok(Json(org.to_json())) } // GET /api/collections?writeOnly=false #[get("/collections")] -async fn get_user_collections(headers: Headers, conn: DbConn) -> Json { +async fn get_user_collections(headers: Headers, mut conn: DbConn) -> Json { Json(json!({ "Data": - Collection::find_by_user_uuid(&headers.user.uuid, &conn).await + Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await .iter() .map(Collection::to_json) .collect::(), @@ -241,10 +240,10 @@ async fn get_user_collections(headers: Headers, conn: DbConn) -> Json { } #[get("/organizations//collections")] -async fn get_org_collections(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) -> Json { +async fn get_org_collections(org_id: String, _headers: ManagerHeadersLoose, mut conn: DbConn) -> Json { Json(json!({ "Data": - Collection::find_by_organization(&org_id, &conn).await + Collection::find_by_organization(&org_id, &mut conn).await .iter() .map(Collection::to_json) .collect::(), @@ -258,29 +257,29 @@ async fn post_organization_collections( org_id: String, headers: ManagerHeadersLoose, data: JsonUpcase, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { let data: NewCollectionData = data.into_inner().data; - let org = match Organization::find_by_uuid(&org_id, &conn).await { + let org = match Organization::find_by_uuid(&org_id, &mut conn).await { Some(organization) => organization, None => err!("Can't find organization details"), }; // Get the user_organization record so that we can check if the user has access to all collections. - let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await { + let user_org = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { Some(u) => u, None => err!("User is not part of organization"), }; let collection = Collection::new(org.uuid, data.Name); - collection.save(&conn).await?; + collection.save(&mut conn).await?; // If the user doesn't have access to all collections, only in case of a Manger, // then we need to save the creating user uuid (Manager) to the users_collection table. // Else the user will not have access to his own created collection. if !user_org.access_all { - CollectionUser::save(&headers.user.uuid, &collection.uuid, false, false, &conn).await?; + CollectionUser::save(&headers.user.uuid, &collection.uuid, false, false, &mut conn).await?; } Ok(Json(collection.to_json())) @@ -303,16 +302,16 @@ async fn post_organization_collection_update( col_id: String, _headers: ManagerHeaders, data: JsonUpcase, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { let data: NewCollectionData = data.into_inner().data; - let org = match Organization::find_by_uuid(&org_id, &conn).await { + let org = match Organization::find_by_uuid(&org_id, &mut conn).await { Some(organization) => organization, None => err!("Can't find organization details"), }; - let mut collection = match Collection::find_by_uuid(&col_id, &conn).await { + let mut collection = match Collection::find_by_uuid(&col_id, &mut conn).await { Some(collection) => collection, None => err!("Collection not found"), }; @@ -322,7 +321,7 @@ async fn post_organization_collection_update( } collection.name = data.Name; - collection.save(&conn).await?; + collection.save(&mut conn).await?; Ok(Json(collection.to_json())) } @@ -333,9 +332,9 @@ async fn delete_organization_collection_user( col_id: String, org_user_id: String, _headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { - let collection = match Collection::find_by_uuid(&col_id, &conn).await { + let collection = match Collection::find_by_uuid(&col_id, &mut conn).await { None => err!("Collection not found"), Some(collection) => { if collection.org_uuid == org_id { @@ -346,12 +345,12 @@ async fn delete_organization_collection_user( } }; - match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn).await { + match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &mut 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, &conn).await { + match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &mut conn).await { None => err!("User not assigned to collection"), - Some(col_user) => col_user.delete(&conn).await, + Some(col_user) => col_user.delete(&mut conn).await, } } } @@ -373,13 +372,13 @@ async fn delete_organization_collection( org_id: String, col_id: String, _headers: ManagerHeaders, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { - match Collection::find_by_uuid(&col_id, &conn).await { + match Collection::find_by_uuid(&col_id, &mut conn).await { None => err!("Collection not found"), Some(collection) => { if collection.org_uuid == org_id { - collection.delete(&conn).await + collection.delete(&mut conn).await } else { err!("Collection and Organization id do not match") } @@ -410,9 +409,9 @@ async fn get_org_collection_detail( org_id: String, coll_id: String, headers: ManagerHeaders, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { - match Collection::find_by_uuid_and_user(&coll_id, &headers.user.uuid, &conn).await { + match Collection::find_by_uuid_and_user(&coll_id, headers.user.uuid.clone(), &mut conn).await { None => err!("Collection not found"), Some(collection) => { if collection.org_uuid != org_id { @@ -425,23 +424,27 @@ async fn get_org_collection_detail( } #[get("/organizations//collections//users")] -async fn get_collection_users(org_id: String, coll_id: String, _headers: ManagerHeaders, conn: DbConn) -> JsonResult { +async fn get_collection_users( + org_id: String, + coll_id: String, + _headers: ManagerHeaders, + mut 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, &conn).await { + let collection = match Collection::find_by_uuid_and_org(&coll_id, &org_id, &mut conn).await { None => err!("Collection not found in Organization"), Some(collection) => collection, }; - let user_list = stream::iter(CollectionUser::find_by_collection(&collection.uuid, &conn).await) - .then(|col_user| async { - let col_user = col_user; // Move out this single variable - UserOrganization::find_by_user_and_org(&col_user.user_uuid, &org_id, &conn) + let mut user_list = Vec::new(); + for col_user in CollectionUser::find_by_collection(&collection.uuid, &mut conn).await { + user_list.push( + UserOrganization::find_by_user_and_org(&col_user.user_uuid, &org_id, &mut conn) .await .unwrap() - .to_json_user_access_restrictions(&col_user) - }) - .collect::>() - .await; + .to_json_user_access_restrictions(&col_user), + ); + } Ok(Json(json!(user_list))) } @@ -452,19 +455,19 @@ async fn put_collection_users( coll_id: String, data: JsonUpcaseVec, _headers: ManagerHeaders, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { // Get org and collection, check that collection is from org - if Collection::find_by_uuid_and_org(&coll_id, &org_id, &conn).await.is_none() { + if Collection::find_by_uuid_and_org(&coll_id, &org_id, &mut conn).await.is_none() { err!("Collection not found in Organization") } // Delete all the user-collections - CollectionUser::delete_all_by_collection(&coll_id, &conn).await?; + CollectionUser::delete_all_by_collection(&coll_id, &mut 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, &conn).await { + let user = match UserOrganization::find_by_uuid(&d.Id, &mut conn).await { Some(u) => u, None => err!("User is not part of organization"), }; @@ -473,7 +476,7 @@ async fn put_collection_users( continue; } - CollectionUser::save(&user.user_uuid, &coll_id, d.ReadOnly, d.HidePasswords, &conn).await?; + CollectionUser::save(&user.user_uuid, &coll_id, d.ReadOnly, d.HidePasswords, &mut conn).await?; } Ok(()) @@ -486,17 +489,15 @@ struct OrgIdData { } #[get("/ciphers/organization-details?")] -async fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> Json { - let ciphers = Cipher::find_by_org(&data.organization_id, &conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::Organization, &conn).await; - - let ciphers_json = stream::iter(ciphers) - .then(|c| async { - let c = c; // Move out this single variable - c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &conn).await - }) - .collect::>() - .await; +async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) -> Json { + let ciphers = Cipher::find_by_org(&data.organization_id, &mut conn).await; + let cipher_sync_data = + CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::Organization, &mut conn).await; + + let mut ciphers_json = Vec::new(); + for c in ciphers { + ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); + } Json(json!({ "Data": ciphers_json, @@ -506,14 +507,11 @@ async fn get_org_details(data: OrgIdData, headers: Headers, conn: DbConn) -> Jso } #[get("/organizations//users")] -async fn get_org_users(org_id: String, _headers: ManagerHeadersLoose, conn: DbConn) -> Json { - let users_json = stream::iter(UserOrganization::find_by_org(&org_id, &conn).await) - .then(|u| async { - let u = u; // Move out this single variable - u.to_json_user_details(&conn).await - }) - .collect::>() - .await; +async fn get_org_users(org_id: String, _headers: ManagerHeadersLoose, mut conn: DbConn) -> Json { + let mut users_json = Vec::new(); + for u in UserOrganization::find_by_org(&org_id, &mut conn).await { + users_json.push(u.to_json_user_details(&mut conn).await); + } Json(json!({ "Data": users_json, @@ -527,11 +525,11 @@ async fn post_org_keys( org_id: String, data: JsonUpcase, _headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { let data: OrgKeyData = data.into_inner().data; - let mut org = match Organization::find_by_uuid(&org_id, &conn).await { + let mut org = match Organization::find_by_uuid(&org_id, &mut conn).await { Some(organization) => { if organization.private_key.is_some() && organization.public_key.is_some() { err!("Organization Keys already exist") @@ -544,7 +542,7 @@ async fn post_org_keys( org.private_key = Some(data.EncryptedPrivateKey); org.public_key = Some(data.PublicKey); - org.save(&conn).await?; + org.save(&mut conn).await?; Ok(Json(json!({ "Object": "organizationKeys", @@ -571,7 +569,12 @@ struct InviteData { } #[post("/organizations//users/invite", data = "")] -async fn send_invite(org_id: String, data: JsonUpcase, headers: AdminHeaders, conn: DbConn) -> EmptyResult { +async fn send_invite( + org_id: String, + data: JsonUpcase, + headers: AdminHeaders, + mut conn: DbConn, +) -> EmptyResult { let data: InviteData = data.into_inner().data; let new_type = match UserOrgType::from_str(&data.Type.into_string()) { @@ -590,7 +593,7 @@ async fn send_invite(org_id: String, data: JsonUpcase, headers: Admi } else { UserOrgStatus::Accepted as i32 // Automatically mark user as accepted if no email invites }; - let user = match User::find_by_mail(&email, &conn).await { + let user = match User::find_by_mail(&email, &mut conn).await { None => { if !CONFIG.invitations_allowed() { err!(format!("User does not exist: {}", email)) @@ -602,16 +605,16 @@ async fn send_invite(org_id: String, data: JsonUpcase, headers: Admi if !CONFIG.mail_enabled() { let invitation = Invitation::new(email.clone()); - invitation.save(&conn).await?; + invitation.save(&mut conn).await?; } let mut user = User::new(email.clone()); - user.save(&conn).await?; + user.save(&mut conn).await?; user_org_status = UserOrgStatus::Invited as i32; user } Some(user) => { - if UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn).await.is_some() { + if UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &mut conn).await.is_some() { err!(format!("User already in organization: {}", email)) } else { user @@ -628,20 +631,20 @@ async fn send_invite(org_id: String, data: JsonUpcase, headers: Admi // 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, &conn).await { + match Collection::find_by_uuid_and_org(&col.Id, &org_id, &mut conn).await { None => err!("Collection not found in Organization"), Some(collection) => { - CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, col.HidePasswords, &conn) + CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, col.HidePasswords, &mut conn) .await?; } } } } - new_user.save(&conn).await?; + new_user.save(&mut conn).await?; if CONFIG.mail_enabled() { - let org_name = match Organization::find_by_uuid(&org_id, &conn).await { + let org_name = match Organization::find_by_uuid(&org_id, &mut conn).await { Some(org) => org.name, None => err!("Error looking up organization"), }; @@ -666,13 +669,13 @@ async fn bulk_reinvite_user( org_id: String, data: JsonUpcase, headers: AdminHeaders, - conn: DbConn, + mut 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, &conn).await { + let err_msg = match _reinvite_user(&org_id, &org_user_id, &headers.user.email, &mut conn).await { Ok(_) => String::from(""), Err(e) => format!("{:?}", e), }; @@ -694,11 +697,11 @@ async fn bulk_reinvite_user( } #[post("/organizations//users//reinvite")] -async fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult { - _reinvite_user(&org_id, &user_org, &headers.user.email, &conn).await +async fn reinvite_user(org_id: String, user_org: String, 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, invited_by_email: &str, conn: &DbConn) -> EmptyResult { +async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &mut DbConn) -> EmptyResult { if !CONFIG.invitations_allowed() { err!("Invitations are not allowed.") } @@ -755,18 +758,18 @@ async fn accept_invite( org_id: String, _org_user_id: String, data: JsonUpcase, - conn: DbConn, + mut 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, &conn).await { + match User::find_by_mail(&claims.email, &mut conn).await { Some(_) => { - Invitation::take(&claims.email, &conn).await; + Invitation::take(&claims.email, &mut 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, &conn).await { + let mut user_org = match UserOrganization::find_by_uuid_and_org(user_org, org, &mut conn).await { Some(user_org) => user_org, None => err!("Error accepting the invitation"), }; @@ -778,7 +781,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, &conn).await { + match OrgPolicy::is_user_allowed(&user_org.user_uuid, &org_id, false, &mut conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { err!("You cannot join this organization until you enable two-step login on your user account"); @@ -790,7 +793,7 @@ async fn accept_invite( } user_org.status = UserOrgStatus::Accepted as i32; - user_org.save(&conn).await?; + user_org.save(&mut conn).await?; } } None => err!("Invited user not found"), @@ -799,7 +802,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, &conn).await { + org_name = match Organization::find_by_uuid(org_id, &mut conn).await { Some(org) => org.name, None => err!("Organization not found."), }; @@ -821,7 +824,7 @@ async fn bulk_confirm_invite( org_id: String, data: JsonUpcase, headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> Json { let data = data.into_inner().data; @@ -831,7 +834,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, &conn).await { + let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn).await { Ok(_) => String::from(""), Err(e) => format!("{:?}", e), }; @@ -861,11 +864,11 @@ async fn confirm_invite( org_user_id: String, data: JsonUpcase, headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> 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, &conn).await + _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn).await } async fn _confirm_invite( @@ -873,7 +876,7 @@ async fn _confirm_invite( org_user_id: &str, key: &str, headers: &AdminHeaders, - conn: &DbConn, + conn: &mut DbConn, ) -> EmptyResult { if key.is_empty() || org_user_id.is_empty() { err!("Key or UserId is not set, unable to process request"); @@ -925,13 +928,13 @@ async fn _confirm_invite( } #[get("/organizations//users/")] -async fn get_user(org_id: String, org_user_id: String, _headers: AdminHeaders, conn: DbConn) -> JsonResult { - let user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &conn).await { +async fn get_user(org_id: String, org_user_id: String, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { + let user = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &mut conn).await { Some(user) => user, None => err!("The specified user isn't a member of the organization"), }; - Ok(Json(user.to_json_details(&conn).await)) + Ok(Json(user.to_json_details(&mut conn).await)) } #[derive(Deserialize)] @@ -959,7 +962,7 @@ async fn edit_user( org_user_id: String, data: JsonUpcase, headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { let data: EditUserData = data.into_inner().data; @@ -968,7 +971,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, &conn).await { + let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(&org_user_id, &org_id, &mut conn).await { Some(user) => user, None => err!("The specified user isn't member of the organization"), }; @@ -986,7 +989,7 @@ async fn edit_user( if user_to_edit.atype == UserOrgType::Owner && new_type != UserOrgType::Owner { // Removing owner permmission, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &conn).await <= 1 { + if UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &mut conn).await <= 1 { err!("Can't delete the last owner") } } @@ -994,7 +997,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, &conn).await { + match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, &org_id, true, &mut conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { err!("You cannot modify this user to this type because it has no two-step login method activated"); @@ -1009,14 +1012,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, &conn).await { - c.delete(&conn).await?; + 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?; } // 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, &conn).await { + match Collection::find_by_uuid_and_org(&col.Id, &org_id, &mut conn).await { None => err!("Collection not found in Organization"), Some(collection) => { CollectionUser::save( @@ -1024,7 +1027,7 @@ async fn edit_user( &collection.uuid, col.ReadOnly, col.HidePasswords, - &conn, + &mut conn, ) .await?; } @@ -1032,7 +1035,7 @@ async fn edit_user( } } - user_to_edit.save(&conn).await + user_to_edit.save(&mut conn).await } #[delete("/organizations//users", data = "")] @@ -1040,13 +1043,13 @@ async fn bulk_delete_user( org_id: String, data: JsonUpcase, headers: AdminHeaders, - conn: DbConn, + mut 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 _delete_user(&org_id, &org_user_id, &headers, &conn).await { + let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn).await { Ok(_) => String::from(""), Err(e) => format!("{:?}", e), }; @@ -1068,11 +1071,11 @@ async fn bulk_delete_user( } #[delete("/organizations//users/")] -async fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult { - _delete_user(&org_id, &org_user_id, &headers, &conn).await +async fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _delete_user(&org_id, &org_user_id, &headers, &mut conn).await } -async fn _delete_user(org_id: &str, org_user_id: &str, headers: &AdminHeaders, conn: &DbConn) -> EmptyResult { +async fn _delete_user(org_id: &str, org_user_id: &str, headers: &AdminHeaders, conn: &mut DbConn) -> EmptyResult { let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { Some(user) => user, None => err!("User to delete isn't member of the organization"), @@ -1102,7 +1105,7 @@ async fn bulk_public_keys( org_id: String, data: JsonUpcase, _headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> Json { let data: OrgBulkIds = data.into_inner().data; @@ -1111,8 +1114,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 folowing steps. for user_org_id in data.Ids { - 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 { + 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 { Some(user) => bulk_response.push(json!( { "Object": "organizationUserPublicKeyResponseModel", @@ -1159,23 +1162,21 @@ async fn post_org_import( query: OrgIdData, data: JsonUpcase, headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { let data: ImportData = data.into_inner().data; let org_id = query.organization_id; - let collections = stream::iter(data.Collections) - .then(|coll| async { - let collection = Collection::new(org_id.clone(), coll.Name); - if collection.save(&conn).await.is_err() { - err!("Failed to create Collection"); - } - - Ok(collection) - }) - .collect::>() - .await; + let mut collections = Vec::new(); + for coll in data.Collections { + let collection = Collection::new(org_id.clone(), coll.Name); + if collection.save(&mut conn).await.is_err() { + collections.push(Err(Error::new("Failed to create Collection", "Failed to create Collection"))); + } else { + collections.push(Ok(collection)); + } + } // Read the relations between collections and ciphers let mut relations = Vec::new(); @@ -1185,14 +1186,12 @@ async fn post_org_import( let headers: Headers = headers.into(); - let ciphers = stream::iter(data.Ciphers) - .then(|cipher_data| async { - let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); - update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::None).await.ok(); - cipher - }) - .collect::>() - .await; + 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(); + ciphers.push(cipher); + } // Assign the collections for (cipher_index, coll_index) in relations { @@ -1203,16 +1202,16 @@ async fn post_org_import( Err(_) => err!("Failed to assign to collection"), }; - CollectionCipher::save(cipher_id, coll_id, &conn).await?; + CollectionCipher::save(cipher_id, coll_id, &mut conn).await?; } let mut user = headers.user; - user.update_revision(&conn).await + user.update_revision(&mut conn).await } #[get("/organizations//policies")] -async fn list_policies(org_id: String, _headers: AdminHeaders, conn: DbConn) -> Json { - let policies = OrgPolicy::find_by_org(&org_id, &conn).await; +async fn list_policies(org_id: String, _headers: AdminHeaders, mut conn: DbConn) -> Json { + let policies = OrgPolicy::find_by_org(&org_id, &mut conn).await; let policies_json: Vec = policies.iter().map(OrgPolicy::to_json).collect(); Json(json!({ @@ -1223,7 +1222,7 @@ async fn list_policies(org_id: String, _headers: AdminHeaders, conn: DbConn) -> } #[get("/organizations//policies/token?")] -async fn list_policies_token(org_id: String, token: String, conn: DbConn) -> JsonResult { +async fn list_policies_token(org_id: String, token: String, mut conn: DbConn) -> JsonResult { let invite = crate::auth::decode_invite(&token)?; let invite_org_id = match invite.org_id { @@ -1236,7 +1235,7 @@ async fn list_policies_token(org_id: String, token: String, 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, &conn).await; + let policies = OrgPolicy::find_by_org(&org_id, &mut conn).await; let policies_json: Vec = policies.iter().map(OrgPolicy::to_json).collect(); Ok(Json(json!({ @@ -1247,13 +1246,13 @@ async fn list_policies_token(org_id: String, token: String, conn: DbConn) -> Jso } #[get("/organizations//policies/")] -async fn get_policy(org_id: String, pol_type: i32, _headers: AdminHeaders, conn: DbConn) -> JsonResult { +async fn get_policy(org_id: String, pol_type: i32, _headers: AdminHeaders, mut 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, &conn).await { + let policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type_enum, &mut conn).await { Some(p) => p, None => OrgPolicy::new(org_id, pol_type_enum, "{}".to_string()), }; @@ -1275,7 +1274,7 @@ async fn put_policy( pol_type: i32, data: Json, _headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { let data: PolicyData = data.into_inner(); @@ -1286,8 +1285,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, &conn).await.into_iter() { - let user_twofactor_disabled = TwoFactor::find_by_user(&member.user_uuid, &conn).await.is_empty(); + 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(); // 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. @@ -1296,46 +1295,46 @@ async fn put_policy( && member.status != UserOrgStatus::Invited as i32 { if CONFIG.mail_enabled() { - let org = Organization::find_by_uuid(&member.org_uuid, &conn).await.unwrap(); - let user = User::find_by_uuid(&member.user_uuid, &conn).await.unwrap(); + 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(); mail::send_2fa_removed_from_org(&user.email, &org.name).await?; } - member.delete(&conn).await?; + member.delete(&mut 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, &conn).await.into_iter() { + for member in UserOrganization::find_by_org(&org_id, &mut 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, &conn).await > 1 + && UserOrganization::count_accepted_and_confirmed_by_user(&member.user_uuid, &mut conn).await > 1 { if CONFIG.mail_enabled() { - let org = Organization::find_by_uuid(&member.org_uuid, &conn).await.unwrap(); - let user = User::find_by_uuid(&member.user_uuid, &conn).await.unwrap(); + 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(); mail::send_single_org_removed_from_org(&user.email, &org.name).await?; } - member.delete(&conn).await?; + member.delete(&mut conn).await?; } } } - let mut policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type_enum, &conn).await { + let mut policy = match OrgPolicy::find_by_org_and_type(&org_id, pol_type_enum, &mut conn).await { Some(p) => p, None => OrgPolicy::new(org_id, pol_type_enum, "{}".to_string()), }; policy.enabled = data.enabled; policy.data = serde_json::to_string(&data.data)?; - policy.save(&conn).await?; + policy.save(&mut conn).await?; Ok(Json(policy.to_json())) } @@ -1408,7 +1407,7 @@ struct OrgImportData { } #[post("/organizations//import", data = "")] -async fn import(org_id: String, data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { +async fn import(org_id: String, data: JsonUpcase, headers: Headers, mut 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 @@ -1417,7 +1416,7 @@ async fn import(org_id: String, data: JsonUpcase, headers: Header // as opposed to upstream which only removes auto-imported users. // User needs to be admin or owner to use the Directry Connector - match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await { + match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut 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"), @@ -1426,13 +1425,14 @@ async fn import(org_id: String, data: JsonUpcase, headers: Header 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, &conn).await { - user_org.delete(&conn).await?; + if let Some(user_org) = UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &mut conn).await + { + user_org.delete(&mut 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, &conn).await.is_none() { - if let Some(user) = User::find_by_mail(&user_data.Email, &conn).await { + } 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 { let user_org_status = if CONFIG.mail_enabled() { UserOrgStatus::Invited as i32 } else { @@ -1444,10 +1444,10 @@ async fn import(org_id: String, data: JsonUpcase, headers: Header new_org_user.atype = UserOrgType::User as i32; new_org_user.status = user_org_status; - new_org_user.save(&conn).await?; + new_org_user.save(&mut conn).await?; if CONFIG.mail_enabled() { - let org_name = match Organization::find_by_uuid(&org_id, &conn).await { + let org_name = match Organization::find_by_uuid(&org_id, &mut conn).await { Some(org) => org.name, None => err!("Error looking up organization"), }; @@ -1468,10 +1468,10 @@ async fn import(org_id: String, data: JsonUpcase, headers: Header // 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, &conn).await { - if let Some(user_email) = User::find_by_uuid(&user_org.user_uuid, &conn).await.map(|u| u.email) { + 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) { if !data.Users.iter().any(|u| u.Email == user_email) { - user_org.delete(&conn).await?; + user_org.delete(&mut conn).await?; } } } @@ -1485,9 +1485,9 @@ async fn deactivate_organization_user( org_id: String, org_user_id: String, headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { - _deactivate_organization_user(&org_id, &org_user_id, &headers, &conn).await + _deactivate_organization_user(&org_id, &org_user_id, &headers, &mut conn).await } #[put("/organizations//users/deactivate", data = "")] @@ -1495,7 +1495,7 @@ async fn bulk_deactivate_organization_user( org_id: String, data: JsonUpcase, headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> Json { let data = data.into_inner().data; @@ -1504,7 +1504,7 @@ async fn bulk_deactivate_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 _deactivate_organization_user(&org_id, org_user_id, &headers, &conn).await { + let err_msg = match _deactivate_organization_user(&org_id, org_user_id, &headers, &mut conn).await { Ok(_) => String::from(""), Err(e) => format!("{:?}", e), }; @@ -1532,7 +1532,7 @@ async fn _deactivate_organization_user( org_id: &str, org_user_id: &str, headers: &AdminHeaders, - conn: &DbConn, + conn: &mut 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 => { @@ -1562,9 +1562,9 @@ async fn activate_organization_user( org_id: String, org_user_id: String, headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> EmptyResult { - _activate_organization_user(&org_id, &org_user_id, &headers, &conn).await + _activate_organization_user(&org_id, &org_user_id, &headers, &mut conn).await } #[put("/organizations//users/activate", data = "")] @@ -1572,7 +1572,7 @@ async fn bulk_activate_organization_user( org_id: String, data: JsonUpcase, headers: AdminHeaders, - conn: DbConn, + mut conn: DbConn, ) -> Json { let data = data.into_inner().data; @@ -1581,7 +1581,7 @@ async fn bulk_activate_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 _activate_organization_user(&org_id, org_user_id, &headers, &conn).await { + let err_msg = match _activate_organization_user(&org_id, org_user_id, &headers, &mut conn).await { Ok(_) => String::from(""), Err(e) => format!("{:?}", e), }; @@ -1609,7 +1609,7 @@ async fn _activate_organization_user( org_id: &str, org_user_id: &str, headers: &AdminHeaders, - conn: &DbConn, + conn: &mut 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 => { diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs index 3d150b31..a9c0d9ee 100644 --- a/src/api/core/sends.rs +++ b/src/api/core/sends.rs @@ -34,8 +34,8 @@ pub fn routes() -> Vec { pub async fn purge_sends(pool: DbPool) { debug!("Purging sends"); - if let Ok(conn) = pool.get().await { - Send::purge(&conn).await; + if let Ok(mut conn) = pool.get().await { + Send::purge(&mut conn).await; } else { error!("Failed to get DB connection while purging sends") } @@ -68,7 +68,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: &DbConn) -> EmptyResult { +async fn enforce_disable_send_policy(headers: &Headers, conn: &mut DbConn) -> EmptyResult { let user_uuid = &headers.user.uuid; if !CONFIG.sends_allowed() || OrgPolicy::is_applicable_to_user(user_uuid, OrgPolicyType::DisableSend, None, conn).await @@ -84,7 +84,7 @@ async fn enforce_disable_send_policy(headers: &Headers, conn: &DbConn) -> EmptyR /// 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: &DbConn) -> EmptyResult { +async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &mut 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 { @@ -136,8 +136,8 @@ fn create_send(data: SendData, user_uuid: String) -> ApiResult { } #[get("/sends")] -async fn get_sends(headers: Headers, conn: DbConn) -> Json { - let sends = Send::find_by_user(&headers.user.uuid, &conn); +async fn get_sends(headers: Headers, mut conn: DbConn) -> Json { + let sends = Send::find_by_user(&headers.user.uuid, &mut conn); let sends_json: Vec = sends.await.iter().map(|s| s.to_json()).collect(); Json(json!({ @@ -148,8 +148,8 @@ async fn get_sends(headers: Headers, conn: DbConn) -> Json { } #[get("/sends/")] -async fn get_send(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { - let send = match Send::find_by_uuid(&uuid, &conn).await { +async fn get_send(uuid: String, headers: Headers, mut conn: DbConn) -> JsonResult { + let send = match Send::find_by_uuid(&uuid, &mut conn).await { Some(send) => send, None => err!("Send not found"), }; @@ -162,19 +162,19 @@ async fn get_send(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { } #[post("/sends", data = "")] -async fn post_send(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { - enforce_disable_send_policy(&headers, &conn).await?; +async fn post_send(data: JsonUpcase, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { + enforce_disable_send_policy(&headers, &mut conn).await?; let data: SendData = data.into_inner().data; - enforce_disable_hide_email_policy(&data, &headers, &conn).await?; + enforce_disable_hide_email_policy(&data, &headers, &mut 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(&conn).await?; - nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&conn).await).await; + send.save(&mut conn).await?; + nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&mut conn).await).await; Ok(Json(send.to_json())) } @@ -186,8 +186,8 @@ struct UploadData<'f> { } #[post("/sends/file", format = "multipart/form-data", data = "")] -async fn post_send_file(data: Form>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { - enforce_disable_send_policy(&headers, &conn).await?; +async fn post_send_file(data: Form>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { + enforce_disable_send_policy(&headers, &mut conn).await?; let UploadData { model, @@ -195,7 +195,7 @@ async fn post_send_file(data: Form>, headers: Headers, conn: DbCo } = data.into_inner(); let model = model.into_inner().data; - enforce_disable_hide_email_policy(&model, &headers, &conn).await?; + enforce_disable_hide_email_policy(&model, &headers, &mut conn).await?; // Get the file length and add an extra 5% to avoid issues const SIZE_525_MB: u64 = 550_502_400; @@ -203,7 +203,7 @@ async fn post_send_file(data: Form>, headers: Headers, conn: DbCo 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, &conn).await; + let left = (limit_kb * 1024) - Attachment::size_by_user(&headers.user.uuid, &mut conn).await; if left <= 0 { err!("Attachment storage limit reached! Delete some attachments to free up space") } @@ -251,8 +251,8 @@ async fn post_send_file(data: Form>, headers: Headers, conn: DbCo send.data = serde_json::to_string(&data_value)?; // Save the changes in the database - send.save(&conn).await?; - nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn).await).await; + send.save(&mut conn).await?; + nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await).await; Ok(Json(send.to_json())) } @@ -264,8 +264,13 @@ pub struct SendAccessData { } #[post("/sends/access/", data = "")] -async fn post_access(access_id: String, data: JsonUpcase, conn: DbConn, ip: ClientIp) -> JsonResult { - let mut send = match Send::find_by_access_id(&access_id, &conn).await { +async fn post_access( + access_id: String, + data: JsonUpcase, + mut conn: DbConn, + ip: ClientIp, +) -> JsonResult { + let mut send = match Send::find_by_access_id(&access_id, &mut conn).await { Some(s) => s, None => err_code!(SEND_INACCESSIBLE_MSG, 404), }; @@ -303,9 +308,9 @@ async fn post_access(access_id: String, data: JsonUpcase, conn: send.access_count += 1; } - send.save(&conn).await?; + send.save(&mut conn).await?; - Ok(Json(send.to_json_access(&conn).await)) + Ok(Json(send.to_json_access(&mut conn).await)) } #[post("/sends//access/file/", data = "")] @@ -314,9 +319,9 @@ async fn post_access_file( file_id: String, data: JsonUpcase, host: Host, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { - let mut send = match Send::find_by_uuid(&send_id, &conn).await { + let mut send = match Send::find_by_uuid(&send_id, &mut conn).await { Some(s) => s, None => err_code!(SEND_INACCESSIBLE_MSG, 404), }; @@ -351,7 +356,7 @@ async fn post_access_file( send.access_count += 1; - send.save(&conn).await?; + send.save(&mut conn).await?; let token_claims = crate::auth::generate_send_claims(&send_id, &file_id); let token = crate::auth::encode_jwt(&token_claims); @@ -377,15 +382,15 @@ async fn put_send( id: String, data: JsonUpcase, headers: Headers, - conn: DbConn, + mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { - enforce_disable_send_policy(&headers, &conn).await?; + enforce_disable_send_policy(&headers, &mut conn).await?; let data: SendData = data.into_inner().data; - enforce_disable_hide_email_policy(&data, &headers, &conn).await?; + enforce_disable_hide_email_policy(&data, &headers, &mut conn).await?; - let mut send = match Send::find_by_uuid(&id, &conn).await { + let mut send = match Send::find_by_uuid(&id, &mut conn).await { Some(s) => s, None => err!("Send not found"), }; @@ -432,15 +437,15 @@ async fn put_send( send.set_password(Some(&password)); } - send.save(&conn).await?; - nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn).await).await; + send.save(&mut conn).await?; + nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await).await; Ok(Json(send.to_json())) } #[delete("/sends/")] -async fn delete_send(id: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { - let send = match Send::find_by_uuid(&id, &conn).await { +async fn delete_send(id: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { + let send = match Send::find_by_uuid(&id, &mut conn).await { Some(s) => s, None => err!("Send not found"), }; @@ -449,17 +454,17 @@ async fn delete_send(id: String, headers: Headers, conn: DbConn, nt: Notify<'_>) err!("Send is not owned by user") } - send.delete(&conn).await?; - nt.send_send_update(UpdateType::SyncSendDelete, &send, &send.update_users_revision(&conn).await).await; + send.delete(&mut conn).await?; + nt.send_send_update(UpdateType::SyncSendDelete, &send, &send.update_users_revision(&mut conn).await).await; Ok(()) } #[put("/sends//remove-password")] -async fn put_remove_password(id: String, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { - enforce_disable_send_policy(&headers, &conn).await?; +async fn put_remove_password(id: String, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> JsonResult { + enforce_disable_send_policy(&headers, &mut conn).await?; - let mut send = match Send::find_by_uuid(&id, &conn).await { + let mut send = match Send::find_by_uuid(&id, &mut conn).await { Some(s) => s, None => err!("Send not found"), }; @@ -469,8 +474,8 @@ async fn put_remove_password(id: String, headers: Headers, conn: DbConn, nt: Not } send.set_password(None); - send.save(&conn).await?; - nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&conn).await).await; + send.save(&mut conn).await?; + nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await).await; Ok(Json(send.to_json())) } diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs index 542651dd..7373b581 100644 --- a/src/api/core/two_factor/authenticator.rs +++ b/src/api/core/two_factor/authenticator.rs @@ -21,7 +21,7 @@ pub fn routes() -> Vec { } #[post("/two-factor/get-authenticator", data = "")] -async fn generate_authenticator(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn generate_authenticator(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: PasswordData = data.into_inner().data; let user = headers.user; @@ -30,7 +30,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_, &conn).await; + let twofactor = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await; let (enabled, key) = match twofactor { Some(tf) => (true, tf.data), @@ -57,7 +57,7 @@ async fn activate_authenticator( data: JsonUpcase, headers: Headers, ip: ClientIp, - conn: DbConn, + mut conn: DbConn, ) -> JsonResult { let data: EnableAuthenticatorData = data.into_inner().data; let password_hash = data.MasterPasswordHash; @@ -81,9 +81,9 @@ async fn activate_authenticator( } // Validate the token provided with the key, and save new twofactor - validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &ip, &conn).await?; + validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &ip, &mut conn).await?; - _generate_recover_code(&mut user, &conn).await; + _generate_recover_code(&mut user, &mut conn).await; Ok(Json(json!({ "Enabled": true, @@ -107,7 +107,7 @@ pub async fn validate_totp_code_str( totp_code: &str, secret: &str, ip: &ClientIp, - conn: &DbConn, + conn: &mut DbConn, ) -> EmptyResult { if !totp_code.chars().all(char::is_numeric) { err!("TOTP code is not a number"); @@ -121,7 +121,7 @@ pub async fn validate_totp_code( totp_code: &str, secret: &str, ip: &ClientIp, - conn: &DbConn, + conn: &mut 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 ccfa05be..42cc709e 100644 --- a/src/api/core/two_factor/duo.rs +++ b/src/api/core/two_factor/duo.rs @@ -89,14 +89,14 @@ impl DuoStatus { const DISABLED_MESSAGE_DEFAULT: &str = ""; #[post("/two-factor/get-duo", data = "")] -async fn get_duo(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn get_duo(data: JsonUpcase, headers: Headers, mut 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, &conn).await; + let data = get_user_duo_data(&headers.user.uuid, &mut conn).await; let (enabled, data) = match data { DuoStatus::Global(_) => (true, Some(DuoData::secret())), @@ -152,7 +152,7 @@ fn check_duo_fields_custom(data: &EnableDuoData) -> bool { } #[post("/two-factor/duo", data = "")] -async fn activate_duo(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn activate_duo(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EnableDuoData = data.into_inner().data; let mut user = headers.user; @@ -171,9 +171,9 @@ async fn activate_duo(data: JsonUpcase, headers: Headers, conn: D let type_ = TwoFactorType::Duo; let twofactor = TwoFactor::new(user.uuid.clone(), type_, data_str); - twofactor.save(&conn).await?; + twofactor.save(&mut conn).await?; - _generate_recover_code(&mut user, &conn).await; + _generate_recover_code(&mut user, &mut conn).await; Ok(Json(json!({ "Enabled": true, @@ -223,7 +223,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: &DbConn) -> DuoStatus { +async fn get_user_duo_data(uuid: &str, conn: &mut DbConn) -> DuoStatus { let type_ = TwoFactorType::Duo as i32; // If the user doesn't have an entry, disabled @@ -247,7 +247,7 @@ async fn get_user_duo_data(uuid: &str, conn: &DbConn) -> DuoStatus { } // let (ik, sk, ak, host) = get_duo_keys(); -async fn get_duo_keys_email(email: &str, conn: &DbConn) -> ApiResult<(String, String, String, String)> { +async fn get_duo_keys_email(email: &str, conn: &mut 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(), @@ -257,7 +257,7 @@ async fn get_duo_keys_email(email: &str, conn: &DbConn) -> ApiResult<(String, St Ok((data.ik, data.sk, CONFIG.get_duo_akey(), data.host)) } -pub async fn generate_duo_signature(email: &str, conn: &DbConn) -> ApiResult<(String, String)> { +pub async fn generate_duo_signature(email: &str, conn: &mut DbConn) -> ApiResult<(String, String)> { let now = Utc::now().timestamp(); let (ik, sk, ak, host) = get_duo_keys_email(email, conn).await?; @@ -275,7 +275,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: &DbConn) -> EmptyResult { +pub async fn validate_duo_login(email: &str, response: &str, conn: &mut 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 6b7212e8..90247f53 100644 --- a/src/api/core/two_factor/email.rs +++ b/src/api/core/two_factor/email.rs @@ -28,13 +28,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, conn: DbConn) -> EmptyResult { +async fn send_email_login(data: JsonUpcase, mut 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, &conn).await { + let user = match User::find_by_mail(&data.Email, &mut conn).await { Some(user) => user, None => err!("Username or password is incorrect. Try again."), }; @@ -48,13 +48,13 @@ async fn send_email_login(data: JsonUpcase, conn: DbConn) -> err!("Email 2FA is disabled") } - send_token(&user.uuid, &conn).await?; + send_token(&user.uuid, &mut 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: &DbConn) -> EmptyResult { +pub async fn send_token(user_uuid: &str, conn: &mut 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")?; @@ -73,7 +73,7 @@ pub async fn send_token(user_uuid: &str, conn: &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, conn: DbConn) -> JsonResult { +async fn get_email(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: PasswordData = data.into_inner().data; let user = headers.user; @@ -82,7 +82,7 @@ async fn get_email(data: JsonUpcase, headers: Headers, conn: DbCon } let (enabled, mfa_email) = - match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email as i32, &conn).await { + match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::Email as i32, &mut conn).await { Some(x) => { let twofactor_data = EmailTokenData::from_json(&x.data)?; (true, json!(twofactor_data.email)) @@ -107,7 +107,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, conn: DbConn) -> EmptyResult { +async fn send_email(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> EmptyResult { let data: SendEmailData = data.into_inner().data; let user = headers.user; @@ -121,8 +121,8 @@ async fn send_email(data: JsonUpcase, headers: Headers, conn: DbC let type_ = TwoFactorType::Email as i32; - if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await { - tf.delete(&conn).await?; + if let Some(tf) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { + tf.delete(&mut conn).await?; } let generated_token = crypto::generate_email_token(CONFIG.email_token_size()); @@ -130,7 +130,7 @@ async fn send_email(data: JsonUpcase, headers: Headers, conn: DbC // 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(&conn).await?; + twofactor.save(&mut conn).await?; mail::send_token(&twofactor_data.email, &twofactor_data.last_token.map_res("Token is empty")?).await?; @@ -147,7 +147,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, conn: DbConn) -> JsonResult { +async fn email(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EmailData = data.into_inner().data; let mut user = headers.user; @@ -157,7 +157,7 @@ async fn email(data: JsonUpcase, headers: Headers, conn: DbConn) -> J let type_ = TwoFactorType::EmailVerificationChallenge as i32; let mut twofactor = - TwoFactor::find_by_user_and_type(&user.uuid, type_, &conn).await.map_res("Two factor not found")?; + TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await.map_res("Two factor not found")?; let mut email_data = EmailTokenData::from_json(&twofactor.data)?; @@ -173,9 +173,9 @@ async fn email(data: JsonUpcase, headers: Headers, conn: DbConn) -> J email_data.reset_token(); twofactor.atype = TwoFactorType::Email as i32; twofactor.data = email_data.to_json(); - twofactor.save(&conn).await?; + twofactor.save(&mut conn).await?; - _generate_recover_code(&mut user, &conn).await; + _generate_recover_code(&mut user, &mut conn).await; Ok(Json(json!({ "Email": email_data.email, @@ -185,7 +185,7 @@ async fn email(data: JsonUpcase, headers: Headers, conn: DbConn) -> J } /// 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: &DbConn) -> EmptyResult { +pub async fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, conn: &mut 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 3ecc5454..94938534 100644 --- a/src/api/core/two_factor/mod.rs +++ b/src/api/core/two_factor/mod.rs @@ -31,8 +31,8 @@ pub fn routes() -> Vec { } #[get("/two-factor")] -async fn get_twofactor(headers: Headers, conn: DbConn) -> Json { - let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn).await; +async fn get_twofactor(headers: Headers, mut conn: DbConn) -> Json { + let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &mut conn).await; let twofactors_json: Vec = twofactors.iter().map(TwoFactor::to_json_provider).collect(); Json(json!({ @@ -66,13 +66,13 @@ struct RecoverTwoFactor { } #[post("/two-factor/recover", data = "")] -async fn recover(data: JsonUpcase, conn: DbConn) -> JsonResult { +async fn recover(data: JsonUpcase, mut 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, &conn).await { + let mut user = match User::find_by_mail(&data.Email, &mut conn).await { Some(user) => user, None => err!("Username or password is incorrect. Try again."), }; @@ -88,15 +88,15 @@ async fn recover(data: JsonUpcase, conn: DbConn) -> JsonResult } // Remove all twofactors from the user - TwoFactor::delete_all_by_user(&user.uuid, &conn).await?; + TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?; // Remove the recovery code, not needed without twofactors user.totp_recover = None; - user.save(&conn).await?; + user.save(&mut conn).await?; Ok(Json(json!({}))) } -async fn _generate_recover_code(user: &mut User, conn: &DbConn) { +async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) { if user.totp_recover.is_none() { let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20])); user.totp_recover = Some(totp_recover); @@ -112,7 +112,7 @@ struct DisableTwoFactorData { } #[post("/two-factor/disable", data = "")] -async fn disable_twofactor(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn disable_twofactor(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: DisableTwoFactorData = data.into_inner().data; let password_hash = data.MasterPasswordHash; let user = headers.user; @@ -123,24 +123,24 @@ 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_, &conn).await { - twofactor.delete(&conn).await?; + if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { + twofactor.delete(&mut conn).await?; } - let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &conn).await.is_empty(); + let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty(); if twofactor_disabled { for user_org in - UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, &conn) + UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, &mut conn) .await .into_iter() { if user_org.atype < UserOrgType::Admin { if CONFIG.mail_enabled() { - let org = Organization::find_by_uuid(&user_org.org_uuid, &conn).await.unwrap(); + let org = Organization::find_by_uuid(&user_org.org_uuid, &mut conn).await.unwrap(); mail::send_2fa_removed_from_org(&user.email, &org.name).await?; } - user_org.delete(&conn).await?; + user_org.delete(&mut conn).await?; } } } @@ -164,7 +164,7 @@ pub async fn send_incomplete_2fa_notifications(pool: DbPool) { return; } - let conn = match pool.get().await { + let mut conn = match pool.get().await { Ok(conn) => conn, _ => { error!("Failed to get DB connection in send_incomplete_2fa_notifications()"); @@ -175,9 +175,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, &conn).await; + let incomplete_logins = TwoFactorIncomplete::find_logins_before(&time_before, &mut conn).await; for login in incomplete_logins { - let user = User::find_by_uuid(&login.user_uuid, &conn).await.expect("User not found"); + let user = User::find_by_uuid(&login.user_uuid, &mut 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 @@ -185,6 +185,6 @@ 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(&conn).await.expect("Error deleting incomplete 2FA record"); + login.delete(&mut 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 ab80c235..0d9e5542 100644 --- a/src/api/core/two_factor/webauthn.rs +++ b/src/api/core/two_factor/webauthn.rs @@ -102,7 +102,7 @@ impl WebauthnRegistration { } #[post("/two-factor/get-webauthn", data = "")] -async fn get_webauthn(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn get_webauthn(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { if !CONFIG.domain_set() { err!("`DOMAIN` environment variable is not set. Webauthn disabled") } @@ -111,7 +111,7 @@ async fn get_webauthn(data: JsonUpcase, headers: Headers, conn: Db err!("Invalid password"); } - let (enabled, registrations) = get_webauthn_registrations(&headers.user.uuid, &conn).await?; + let (enabled, registrations) = get_webauthn_registrations(&headers.user.uuid, &mut conn).await?; let registrations_json: Vec = registrations.iter().map(WebauthnRegistration::to_json).collect(); Ok(Json(json!({ @@ -122,12 +122,12 @@ async fn get_webauthn(data: JsonUpcase, headers: Headers, conn: Db } #[post("/two-factor/get-webauthn-challenge", data = "")] -async fn generate_webauthn_challenge(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn generate_webauthn_challenge(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { if !headers.user.check_valid_password(&data.data.MasterPasswordHash) { err!("Invalid password"); } - let registrations = get_webauthn_registrations(&headers.user.uuid, &conn) + let registrations = get_webauthn_registrations(&headers.user.uuid, &mut conn) .await? .1 .into_iter() @@ -144,7 +144,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(&conn).await?; + TwoFactor::new(headers.user.uuid, type_, serde_json::to_string(&state)?).save(&mut conn).await?; let mut challenge_value = serde_json::to_value(challenge.public_key)?; challenge_value["status"] = "ok".into(); @@ -241,7 +241,7 @@ impl From for PublicKeyCredential { } #[post("/two-factor/webauthn", data = "")] -async fn activate_webauthn(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn activate_webauthn(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EnableWebauthnData = data.into_inner().data; let mut user = headers.user; @@ -251,10 +251,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_, &conn).await { + let state = match TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { Some(tf) => { let state: RegistrationState = serde_json::from_str(&tf.data)?; - tf.delete(&conn).await?; + tf.delete(&mut conn).await?; state } None => err!("Can't recover challenge"), @@ -264,7 +264,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, &conn).await?.1; + let mut registrations: Vec<_> = get_webauthn_registrations(&user.uuid, &mut conn).await?.1; // TODO: Check for repeated ID's registrations.push(WebauthnRegistration { id: data.Id.into_i32()?, @@ -276,9 +276,9 @@ 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(&conn) + .save(&mut conn) .await?; - _generate_recover_code(&mut user, &conn).await; + _generate_recover_code(&mut user, &mut conn).await; let keys_json: Vec = registrations.iter().map(WebauthnRegistration::to_json).collect(); Ok(Json(json!({ @@ -301,17 +301,17 @@ struct DeleteU2FData { } #[delete("/two-factor/webauthn", data = "")] -async fn delete_webauthn(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn delete_webauthn(data: JsonUpcase, headers: Headers, mut 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, &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, &mut conn).await { + Some(tf) => tf, + None => err!("Webauthn data not found!"), + }; let mut data: Vec = serde_json::from_str(&tf.data)?; @@ -322,11 +322,12 @@ async fn delete_webauthn(data: JsonUpcase, headers: Headers, conn let removed_item = data.remove(item_pos); tf.data = serde_json::to_string(&data)?; - tf.save(&conn).await?; + tf.save(&mut 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, &conn).await + if let Some(mut u2f) = + TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::U2f as i32, &mut conn).await { let mut data: Vec = match serde_json::from_str(&u2f.data) { Ok(d) => d, @@ -337,7 +338,7 @@ async fn delete_webauthn(data: JsonUpcase, headers: Headers, conn let new_data_str = serde_json::to_string(&data)?; u2f.data = new_data_str; - u2f.save(&conn).await?; + u2f.save(&mut conn).await?; } let keys_json: Vec = data.iter().map(WebauthnRegistration::to_json).collect(); @@ -351,7 +352,7 @@ async fn delete_webauthn(data: JsonUpcase, headers: Headers, conn pub async fn get_webauthn_registrations( user_uuid: &str, - conn: &DbConn, + conn: &mut DbConn, ) -> Result<(bool, Vec), Error> { let type_ = TwoFactorType::Webauthn as i32; match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await { @@ -360,7 +361,7 @@ pub async fn get_webauthn_registrations( } } -pub async fn generate_webauthn_login(user_uuid: &str, conn: &DbConn) -> JsonResult { +pub async fn generate_webauthn_login(user_uuid: &str, conn: &mut DbConn) -> JsonResult { // Load saved credentials let creds: Vec = get_webauthn_registrations(user_uuid, conn).await?.1.into_iter().map(|r| r.credential).collect(); @@ -382,7 +383,7 @@ pub async fn generate_webauthn_login(user_uuid: &str, conn: &DbConn) -> JsonResu Ok(Json(serde_json::to_value(response.public_key)?)) } -pub async fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult { +pub async fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &mut 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 cadb04a9..7994bea0 100644 --- a/src/api/core/two_factor/yubikey.rs +++ b/src/api/core/two_factor/yubikey.rs @@ -78,7 +78,7 @@ fn verify_yubikey_otp(otp: String) -> EmptyResult { } #[post("/two-factor/get-yubikey", data = "")] -async fn generate_yubikey(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn generate_yubikey(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { // Make sure the credentials are set get_yubico_credentials()?; @@ -92,7 +92,7 @@ async fn generate_yubikey(data: JsonUpcase, headers: Headers, conn let user_uuid = &user.uuid; let yubikey_type = TwoFactorType::YubiKey as i32; - let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn).await; + let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &mut conn).await; if let Some(r) = r { let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?; @@ -113,7 +113,7 @@ async fn generate_yubikey(data: JsonUpcase, headers: Headers, conn } #[post("/two-factor/yubikey", data = "")] -async fn activate_yubikey(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +async fn activate_yubikey(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EnableYubikeyData = data.into_inner().data; let mut user = headers.user; @@ -123,7 +123,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, &conn).await { + match TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::YubiKey as i32, &mut conn).await { Some(data) => data, None => TwoFactor::new(user.uuid.clone(), TwoFactorType::YubiKey, String::new()), }; @@ -155,9 +155,9 @@ async fn activate_yubikey(data: JsonUpcase, headers: Headers, }; yubikey_data.data = serde_json::to_string(&yubikey_metadata).unwrap(); - yubikey_data.save(&conn).await?; + yubikey_data.save(&mut conn).await?; - _generate_recover_code(&mut user, &conn).await; + _generate_recover_code(&mut user, &mut conn).await; let mut result = jsonify_yubikeys(yubikey_metadata.Keys); diff --git a/src/api/identity.rs b/src/api/identity.rs index d0a3bcce..a509df87 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -55,21 +55,21 @@ async fn login(data: Form, conn: DbConn, ip: ClientIp) -> JsonResul } } -async fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult { +async fn _refresh_login(data: ConnectData, mut conn: DbConn) -> JsonResult { // Extract token let token = data.refresh_token.unwrap(); // Get device by refresh token - let mut device = Device::find_by_refresh_token(&token, &conn).await.map_res("Invalid refresh token")?; + let mut device = Device::find_by_refresh_token(&token, &mut conn).await.map_res("Invalid refresh token")?; let scope = "api offline_access"; let scope_vec = vec!["api".into(), "offline_access".into()]; // Common - let user = User::find_by_uuid(&device.user_uuid, &conn).await.unwrap(); - let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn).await; + let user = User::find_by_uuid(&device.user_uuid, &mut conn).await.unwrap(); + let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &mut conn).await; let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec); - device.save(&conn).await?; + device.save(&mut conn).await?; Ok(Json(json!({ "access_token": access_token, @@ -87,7 +87,7 @@ async fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult { }))) } -async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult { +async fn _password_login(data: ConnectData, mut conn: DbConn, ip: &ClientIp) -> JsonResult { // Validate scope let scope = data.scope.as_ref().unwrap(); if scope != "api offline_access" { @@ -100,7 +100,7 @@ async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> Json // Get the user let username = data.username.as_ref().unwrap().trim(); - let user = match User::find_by_mail(username, &conn).await { + let user = match User::find_by_mail(username, &mut conn).await { Some(user) => user, None => err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)), }; @@ -131,7 +131,7 @@ async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> Json user.last_verifying_at = Some(now); user.login_verify_count += 1; - if let Err(e) = user.save(&conn).await { + if let Err(e) = user.save(&mut conn).await { error!("Error updating user: {:#?}", e); } @@ -145,9 +145,9 @@ async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> Json err!("Please verify your email before trying again.", format!("IP: {}. Username: {}.", ip.ip, username)) } - let (mut device, new_device) = get_device(&data, &conn, &user).await; + let (mut device, new_device) = get_device(&data, &mut conn, &user).await; - let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, ip, &conn).await?; + let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, ip, &mut conn).await?; if CONFIG.mail_enabled() && new_device { if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device.name).await { @@ -160,9 +160,9 @@ async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> Json } // Common - let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn).await; + let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &mut conn).await; let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec); - device.save(&conn).await?; + device.save(&mut conn).await?; let mut result = json!({ "access_token": access_token, @@ -188,7 +188,7 @@ async fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> Json Ok(Json(result)) } -async fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult { +async fn _api_key_login(data: ConnectData, mut conn: DbConn, ip: &ClientIp) -> JsonResult { // Validate scope let scope = data.scope.as_ref().unwrap(); if scope != "api" { @@ -205,7 +205,7 @@ async fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonR Some(uuid) => uuid, None => err!("Malformed client_id", format!("IP: {}.", ip.ip)), }; - let user = match User::find_by_uuid(user_uuid, &conn).await { + let user = match User::find_by_uuid(user_uuid, &mut conn).await { Some(user) => user, None => err!("Invalid client_id", format!("IP: {}.", ip.ip)), }; @@ -221,7 +221,7 @@ async fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonR err!("Incorrect client_secret", format!("IP: {}. Username: {}.", ip.ip, user.email)) } - let (mut device, new_device) = get_device(&data, &conn, &user).await; + let (mut device, new_device) = get_device(&data, &mut conn, &user).await; if CONFIG.mail_enabled() && new_device { let now = Utc::now().naive_utc(); @@ -235,9 +235,9 @@ async fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonR } // Common - let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &conn).await; + let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, &mut conn).await; let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec); - device.save(&conn).await?; + device.save(&mut conn).await?; info!("User {} logged in successfully via API key. IP: {}", user.email, ip.ip); @@ -259,7 +259,7 @@ async fn _api_key_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonR } /// Retrieves an existing device or creates a new device from ConnectData and the User -async fn get_device(data: &ConnectData, conn: &DbConn, user: &User) -> (Device, bool) { +async fn get_device(data: &ConnectData, conn: &mut DbConn, user: &User) -> (Device, bool) { // On iOS, device_type sends "iOS", on others it sends a number let device_type = util::try_parse_string(data.device_type.as_ref()).unwrap_or(0); let device_id = data.device_identifier.clone().expect("No device id provided"); @@ -283,7 +283,7 @@ async fn twofactor_auth( data: &ConnectData, device: &mut Device, ip: &ClientIp, - conn: &DbConn, + conn: &mut DbConn, ) -> ApiResult> { let twofactors = TwoFactor::find_by_user(user_uuid, conn).await; @@ -355,7 +355,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: &DbConn) -> ApiResult { +async fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &mut DbConn) -> ApiResult { use crate::api::core::two_factor; let mut result = json!({ diff --git a/src/auth.rs b/src/auth.rs index f99fbd39..5cc0ead9 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -340,17 +340,17 @@ impl<'r> FromRequest<'r> for Headers { let device_uuid = claims.device; let user_uuid = claims.sub; - let conn = match DbConn::from_request(request).await { + let mut 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, &conn).await { + let device = match Device::find_by_uuid_and_user(&device_uuid, &user_uuid, &mut conn).await { Some(device) => device, None => err_handler!("Invalid device id"), }; - let user = match User::find_by_uuid(&user_uuid, &conn).await { + let user = match User::find_by_uuid(&user_uuid, &mut conn).await { Some(user) => user, None => err_handler!("Device has no user associated"), }; @@ -372,7 +372,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(&conn).await { + if let Err(e) = user.save(&mut conn).await { error!("Error updating user: {:#?}", e); } err_handler!("Stamp exception is expired") @@ -430,13 +430,13 @@ impl<'r> FromRequest<'r> for OrgHeaders { let headers = try_outcome!(Headers::from_request(request).await); match get_org_id(request) { Some(org_id) => { - let conn = match DbConn::from_request(request).await { + let mut 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, &conn).await { + let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &mut conn).await { Some(user) => { if user.status == UserOrgStatus::Confirmed as i32 { user @@ -542,14 +542,18 @@ impl<'r> FromRequest<'r> for ManagerHeaders { if headers.org_user_type >= UserOrgType::Manager { match get_col_id(request) { Some(col_id) => { - let conn = match DbConn::from_request(request).await { + let mut conn = match DbConn::from_request(request).await { Outcome::Success(conn) => conn, _ => err_handler!("Error getting DB"), }; if !headers.org_user.has_full_access() { - match CollectionUser::find_by_collection_and_user(&col_id, &headers.org_user.user_uuid, &conn) - .await + match CollectionUser::find_by_collection_and_user( + &col_id, + &headers.org_user.user_uuid, + &mut conn, + ) + .await { Some(_) => (), None => err_handler!("The current user isn't a manager for this collection"), diff --git a/src/db/mod.rs b/src/db/mod.rs index 0b3b7a5b..09cbd4b0 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -365,7 +365,7 @@ pub mod models; /// Creates a back-up of the sqlite database /// MySQL/MariaDB and PostgreSQL are not supported. -pub async fn backup_database(conn: &DbConn) -> Result<(), Error> { +pub async fn backup_database(conn: &mut DbConn) -> Result<(), Error> { db_run! {@raw conn: postgresql, mysql { let _ = conn; @@ -383,15 +383,19 @@ pub async fn backup_database(conn: &DbConn) -> Result<(), Error> { } /// Get the SQL Server version -pub async fn get_sql_server_version(conn: &DbConn) -> String { +pub async fn get_sql_server_version(conn: &mut DbConn) -> String { db_run! {@raw conn: postgresql, mysql { - no_arg_sql_function!(version, diesel::sql_types::Text); - diesel::select(version).get_result::(conn).unwrap_or_else(|_| "Unknown".to_string()) + sql_function!{ + fn version() -> diesel::sql_types::Text; + } + diesel::select(version()).get_result::(conn).unwrap_or_else(|_| "Unknown".to_string()) } sqlite { - no_arg_sql_function!(sqlite_version, diesel::sql_types::Text); - diesel::select(sqlite_version).get_result::(conn).unwrap_or_else(|_| "Unknown".to_string()) + sql_function!{ + fn sqlite_version() -> diesel::sql_types::Text; + } + diesel::select(sqlite_version()).get_result::(conn).unwrap_or_else(|_| "Unknown".to_string()) } } } @@ -416,7 +420,8 @@ impl<'r> FromRequest<'r> for DbConn { // https://docs.rs/diesel_migrations/*/diesel_migrations/macro.embed_migrations.html #[cfg(sqlite)] mod sqlite_migrations { - embed_migrations!("migrations/sqlite"); + use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; + pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/sqlite"); pub fn run_migrations() -> Result<(), super::Error> { // Make sure the directory exists @@ -432,52 +437,54 @@ mod sqlite_migrations { use diesel::{Connection, RunQueryDsl}; // Make sure the database is up to date (create if it doesn't exist, or run the migrations) - let connection = diesel::sqlite::SqliteConnection::establish(&crate::CONFIG.database_url())?; + let mut connection = diesel::sqlite::SqliteConnection::establish(&crate::CONFIG.database_url())?; // Disable Foreign Key Checks during migration // Scoped to a connection. diesel::sql_query("PRAGMA foreign_keys = OFF") - .execute(&connection) + .execute(&mut connection) .expect("Failed to disable Foreign Key Checks during migrations"); // Turn on WAL in SQLite if crate::CONFIG.enable_db_wal() { - diesel::sql_query("PRAGMA journal_mode=wal").execute(&connection).expect("Failed to turn on WAL"); + diesel::sql_query("PRAGMA journal_mode=wal").execute(&mut connection).expect("Failed to turn on WAL"); } - embedded_migrations::run_with_output(&connection, &mut std::io::stdout())?; + connection.run_pending_migrations(MIGRATIONS).expect("Error running migrations"); Ok(()) } } #[cfg(mysql)] mod mysql_migrations { - embed_migrations!("migrations/mysql"); + use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; + pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/mysql"); pub fn run_migrations() -> 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 connection = diesel::mysql::MysqlConnection::establish(&crate::CONFIG.database_url())?; + let mut connection = diesel::mysql::MysqlConnection::establish(&crate::CONFIG.database_url())?; // Disable Foreign Key Checks during migration // Scoped to a connection/session. diesel::sql_query("SET FOREIGN_KEY_CHECKS = 0") - .execute(&connection) + .execute(&mut connection) .expect("Failed to disable Foreign Key Checks during migrations"); - embedded_migrations::run_with_output(&connection, &mut std::io::stdout())?; + connection.run_pending_migrations(MIGRATIONS).expect("Error running migrations"); Ok(()) } } #[cfg(postgresql)] mod postgresql_migrations { - embed_migrations!("migrations/postgresql"); + use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; + pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/postgresql"); pub fn run_migrations() -> 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 connection = diesel::pg::PgConnection::establish(&crate::CONFIG.database_url())?; + let mut connection = diesel::pg::PgConnection::establish(&crate::CONFIG.database_url())?; // Disable Foreign Key Checks during migration // FIXME: Per https://www.postgresql.org/docs/12/sql-set-constraints.html, @@ -487,10 +494,10 @@ mod postgresql_migrations { // Migrations that need to disable foreign key checks should run this // from within the migration script itself. diesel::sql_query("SET CONSTRAINTS ALL DEFERRED") - .execute(&connection) + .execute(&mut connection) .expect("Failed to disable Foreign Key Checks during migrations"); - embedded_migrations::run_with_output(&connection, &mut std::io::stdout())?; + 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 1df4d539..325b9a27 100644 --- a/src/db/models/attachment.rs +++ b/src/db/models/attachment.rs @@ -6,9 +6,9 @@ use crate::CONFIG; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "attachments"] - #[changeset_options(treat_none_as_null="true")] - #[primary_key(id)] + #[diesel(table_name = attachments)] + #[diesel(treat_none_as_null = true)] + #[diesel(primary_key(id))] pub struct Attachment { pub id: String, pub cipher_uuid: String, @@ -58,7 +58,7 @@ use crate::error::MapResult; /// Database methods impl Attachment { - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: sqlite, mysql { match diesel::replace_into(attachments::table) @@ -90,7 +90,7 @@ impl Attachment { } } - pub async fn delete(&self, conn: &DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { crate::util::retry( || diesel::delete(attachments::table.filter(attachments::id.eq(&self.id))).execute(conn), @@ -114,14 +114,14 @@ impl Attachment { }} } - pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut 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: &DbConn) -> Option { + pub async fn find_by_id(id: &str, conn: &mut DbConn) -> Option { db_run! { conn: { attachments::table .filter(attachments::id.eq(id.to_lowercase())) @@ -131,7 +131,7 @@ impl Attachment { }} } - pub async fn find_by_cipher(cipher_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { attachments::table .filter(attachments::cipher_uuid.eq(cipher_uuid)) @@ -141,7 +141,7 @@ impl Attachment { }} } - pub async fn size_by_user(user_uuid: &str, conn: &DbConn) -> i64 { + pub async fn size_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 { db_run! { conn: { let result: Option = attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) @@ -153,7 +153,7 @@ impl Attachment { }} } - pub async fn count_by_user(user_uuid: &str, conn: &DbConn) -> i64 { + pub async fn count_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 { db_run! { conn: { attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) @@ -164,7 +164,7 @@ impl Attachment { }} } - pub async fn size_by_org(org_uuid: &str, conn: &DbConn) -> i64 { + pub async fn size_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { db_run! { conn: { let result: Option = attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) @@ -176,7 +176,7 @@ impl Attachment { }} } - pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 { + pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { db_run! { conn: { attachments::table .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) @@ -187,7 +187,7 @@ impl Attachment { }} } - pub async fn find_all_by_ciphers(cipher_uuids: &Vec, conn: &DbConn) -> Vec { + pub async fn find_all_by_ciphers(cipher_uuids: &Vec, conn: &mut DbConn) -> Vec { db_run! { conn: { attachments::table .filter(attachments::cipher_uuid.eq_any(cipher_uuids)) diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index d5f78fbe..dcf84e44 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -10,9 +10,9 @@ use std::borrow::Cow; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "ciphers"] - #[changeset_options(treat_none_as_null="true")] - #[primary_key(uuid)] + #[diesel(table_name = ciphers)] + #[diesel(treat_none_as_null = true)] + #[diesel(primary_key(uuid))] pub struct Cipher { pub uuid: String, pub created_at: NaiveDateTime, @@ -85,7 +85,7 @@ impl Cipher { host: &str, user_uuid: &str, cipher_sync_data: Option<&CipherSyncData>, - conn: &DbConn, + conn: &mut DbConn, ) -> Value { use crate::util::format_date; @@ -146,7 +146,7 @@ impl Cipher { Cow::from(Vec::with_capacity(0)) } } else { - Cow::from(self.get_collections(user_uuid, conn).await) + Cow::from(self.get_collections(user_uuid.to_string(), conn).await) }; // There are three types of cipher response models in upstream @@ -207,7 +207,7 @@ impl Cipher { json_object } - pub async fn update_users_revision(&self, conn: &DbConn) -> Vec { + pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec { let mut user_uuids = Vec::new(); match self.user_uuid { Some(ref user_uuid) => { @@ -227,7 +227,7 @@ impl Cipher { user_uuids } - pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { self.update_users_revision(conn).await; self.updated_at = Utc::now().naive_utc(); @@ -262,7 +262,7 @@ impl Cipher { } } - pub async fn delete(&self, conn: &DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { self.update_users_revision(conn).await; FolderCipher::delete_all_by_cipher(&self.uuid, conn).await?; @@ -277,7 +277,7 @@ impl Cipher { }} } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut 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?; @@ -285,7 +285,7 @@ impl Cipher { Ok(()) } - pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { for cipher in Self::find_owned_by_user(user_uuid, conn).await { cipher.delete(conn).await?; } @@ -293,7 +293,7 @@ impl Cipher { } /// Purge all ciphers that are old enough to be auto-deleted. - pub async fn purge_trash(conn: &DbConn) { + pub async fn purge_trash(conn: &mut 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); @@ -303,7 +303,7 @@ impl Cipher { } } - pub async fn move_to_folder(&self, folder_uuid: Option, user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn move_to_folder(&self, folder_uuid: Option, user_uuid: &str, conn: &mut DbConn) -> EmptyResult { User::update_uuid_revision(user_uuid, conn).await; match (self.get_folder_uuid(user_uuid, conn).await, folder_uuid) { @@ -340,7 +340,7 @@ impl Cipher { &self, user_uuid: &str, cipher_sync_data: Option<&CipherSyncData>, - conn: &DbConn, + conn: &mut DbConn, ) -> bool { if let Some(ref org_uuid) = self.organization_uuid { if let Some(cipher_sync_data) = cipher_sync_data { @@ -363,7 +363,7 @@ impl Cipher { &self, user_uuid: &str, cipher_sync_data: Option<&CipherSyncData>, - conn: &DbConn, + conn: &mut 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 @@ -410,7 +410,7 @@ impl Cipher { Some((read_only, hide_passwords)) } - pub async fn get_collections_access_flags(&self, user_uuid: &str, conn: &DbConn) -> Vec<(bool, bool)> { + pub async fn get_collections_access_flags(&self, user_uuid: &str, conn: &mut 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. @@ -427,31 +427,31 @@ impl Cipher { }} } - pub async fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &DbConn) -> bool { + pub async fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &mut 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: &DbConn) -> bool { + pub async fn is_accessible_to_user(&self, user_uuid: &str, conn: &mut 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: &DbConn) -> bool { + pub async fn is_favorite(&self, user_uuid: &str, conn: &mut 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: &DbConn) -> EmptyResult { + pub async fn set_favorite(&self, favorite: Option, user_uuid: &str, conn: &mut 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: &DbConn) -> Option { + pub async fn get_folder_uuid(&self, user_uuid: &str, conn: &mut DbConn) -> Option { db_run! {conn: { folders_ciphers::table .inner_join(folders::table) @@ -463,7 +463,7 @@ impl Cipher { }} } - pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! {conn: { ciphers::table .filter(ciphers::uuid.eq(uuid)) @@ -485,7 +485,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: &DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, visible_only: bool, conn: &mut DbConn) -> Vec { db_run! {conn: { let mut query = ciphers::table .left_join(ciphers_collections::table.on( @@ -520,12 +520,12 @@ impl Cipher { } // Find all ciphers visible to the specified user. - pub async fn find_by_user_visible(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_user_visible(user_uuid: &str, conn: &mut 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: &DbConn) -> Vec { + pub async fn find_owned_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! {conn: { ciphers::table .filter( @@ -536,7 +536,7 @@ impl Cipher { }} } - pub async fn count_owned_by_user(user_uuid: &str, conn: &DbConn) -> i64 { + pub async fn count_owned_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 { db_run! {conn: { ciphers::table .filter(ciphers::user_uuid.eq(user_uuid)) @@ -547,7 +547,7 @@ impl Cipher { }} } - pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec { db_run! {conn: { ciphers::table .filter(ciphers::organization_uuid.eq(org_uuid)) @@ -555,7 +555,7 @@ impl Cipher { }} } - pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 { + pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { db_run! {conn: { ciphers::table .filter(ciphers::organization_uuid.eq(org_uuid)) @@ -566,7 +566,7 @@ impl Cipher { }} } - pub async fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_folder(folder_uuid: &str, conn: &mut DbConn) -> Vec { db_run! {conn: { folders_ciphers::table.inner_join(ciphers::table) .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) @@ -576,7 +576,7 @@ impl Cipher { } /// Find all ciphers that were deleted before the specified datetime. - pub async fn find_deleted_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec { + pub async fn find_deleted_before(dt: &NaiveDateTime, conn: &mut DbConn) -> Vec { db_run! {conn: { ciphers::table .filter(ciphers::deleted_at.lt(dt)) @@ -584,7 +584,7 @@ impl Cipher { }} } - pub async fn get_collections(&self, user_id: &str, conn: &DbConn) -> Vec { + pub async fn get_collections(&self, user_id: String, conn: &mut DbConn) -> Vec { db_run! {conn: { ciphers_collections::table .inner_join(collections::table.on( @@ -592,12 +592,12 @@ impl Cipher { )) .inner_join(users_organizations::table.on( users_organizations::org_uuid.eq(collections::org_uuid).and( - users_organizations::user_uuid.eq(user_id) + users_organizations::user_uuid.eq(user_id.clone()) ) )) .left_join(users_collections::table.on( users_collections::collection_uuid.eq(ciphers_collections::collection_uuid).and( - users_collections::user_uuid.eq(user_id) + users_collections::user_uuid.eq(user_id.clone()) ) )) .filter(ciphers_collections::cipher_uuid.eq(&self.uuid)) @@ -613,7 +613,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: &str, conn: &DbConn) -> Vec<(String, String)> { + pub async fn get_collections_with_cipher_by_user(user_id: String, conn: &mut DbConn) -> Vec<(String, String)> { db_run! {conn: { ciphers_collections::table .inner_join(collections::table.on( @@ -621,12 +621,12 @@ impl Cipher { )) .inner_join(users_organizations::table.on( users_organizations::org_uuid.eq(collections::org_uuid).and( - users_organizations::user_uuid.eq(user_id) + users_organizations::user_uuid.eq(user_id.clone()) ) )) .left_join(users_collections::table.on( users_collections::collection_uuid.eq(ciphers_collections::collection_uuid).and( - users_collections::user_uuid.eq(user_id) + users_collections::user_uuid.eq(user_id.clone()) ) )) .filter(users_collections::user_uuid.eq(user_id).or( // User has access to collection diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index 5d9464fd..8136d456 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -4,8 +4,8 @@ use super::{User, UserOrgStatus, UserOrgType, UserOrganization}; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "collections"] - #[primary_key(uuid)] + #[diesel(table_name = collections)] + #[diesel(primary_key(uuid))] pub struct Collection { pub uuid: String, pub org_uuid: String, @@ -13,8 +13,8 @@ db_object! { } #[derive(Identifiable, Queryable, Insertable)] - #[table_name = "users_collections"] - #[primary_key(user_uuid, collection_uuid)] + #[diesel(table_name = users_collections)] + #[diesel(primary_key(user_uuid, collection_uuid))] pub struct CollectionUser { pub user_uuid: String, pub collection_uuid: String, @@ -23,8 +23,8 @@ db_object! { } #[derive(Identifiable, Queryable, Insertable)] - #[table_name = "ciphers_collections"] - #[primary_key(cipher_uuid, collection_uuid)] + #[diesel(table_name = ciphers_collections)] + #[diesel(primary_key(cipher_uuid, collection_uuid))] pub struct CollectionCipher { pub cipher_uuid: String, pub collection_uuid: String, @@ -56,7 +56,7 @@ impl Collection { &self, user_uuid: &str, cipher_sync_data: Option<&crate::api::core::CipherSyncData>, - conn: &DbConn, + conn: &mut 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) { @@ -89,7 +89,7 @@ use crate::error::MapResult; /// Database methods impl Collection { - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { self.update_users_revision(conn).await; db_run! { conn: @@ -123,7 +123,7 @@ impl Collection { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut 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?; @@ -135,20 +135,20 @@ impl Collection { }} } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut 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: &DbConn) { + pub async fn update_users_revision(&self, conn: &mut 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: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { collections::table .filter(collections::uuid.eq(uuid)) @@ -158,17 +158,17 @@ impl Collection { }} } - pub async fn find_by_user_uuid(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_user_uuid(user_uuid: String, conn: &mut DbConn) -> Vec { db_run! { conn: { collections::table .left_join(users_collections::table.on( users_collections::collection_uuid.eq(collections::uuid).and( - users_collections::user_uuid.eq(user_uuid) + users_collections::user_uuid.eq(user_uuid.clone()) ) )) .left_join(users_organizations::table.on( collections::org_uuid.eq(users_organizations::org_uuid).and( - users_organizations::user_uuid.eq(user_uuid) + users_organizations::user_uuid.eq(user_uuid.clone()) ) )) .filter( @@ -183,11 +183,15 @@ impl Collection { }} } - 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, conn).await.into_iter().filter(|c| c.org_uuid == org_uuid).collect() + pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Vec { + Self::find_by_user_uuid(user_uuid.to_owned(), conn) + .await + .into_iter() + .filter(|c| c.org_uuid == org_uuid) + .collect() } - pub async fn find_by_organization(org_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_organization(org_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { collections::table .filter(collections::org_uuid.eq(org_uuid)) @@ -197,7 +201,7 @@ impl Collection { }} } - pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { collections::table .filter(collections::uuid.eq(uuid)) @@ -209,12 +213,12 @@ impl Collection { }} } - pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: String, conn: &mut DbConn) -> Option { db_run! { conn: { collections::table .left_join(users_collections::table.on( users_collections::collection_uuid.eq(collections::uuid).and( - users_collections::user_uuid.eq(user_uuid) + users_collections::user_uuid.eq(user_uuid.clone()) ) )) .left_join(users_organizations::table.on( @@ -235,7 +239,7 @@ impl Collection { }} } - pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &DbConn) -> bool { + pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn).await { None => false, // Not in Org Some(user_org) => { @@ -257,7 +261,7 @@ impl Collection { } } - pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &DbConn) -> bool { + pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool { match UserOrganization::find_by_user_and_org(user_uuid, &self.org_uuid, conn).await { None => true, // Not in Org Some(user_org) => { @@ -282,7 +286,7 @@ impl Collection { /// Database methods impl CollectionUser { - pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_collections::table .filter(users_collections::user_uuid.eq(user_uuid)) @@ -300,7 +304,7 @@ impl CollectionUser { collection_uuid: &str, read_only: bool, hide_passwords: bool, - conn: &DbConn, + conn: &mut DbConn, ) -> EmptyResult { User::update_uuid_revision(user_uuid, conn).await; @@ -353,7 +357,7 @@ impl CollectionUser { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; db_run! { conn: { @@ -367,7 +371,7 @@ impl CollectionUser { }} } - pub async fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_collection(collection_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_collections::table .filter(users_collections::collection_uuid.eq(collection_uuid)) @@ -378,7 +382,11 @@ impl CollectionUser { }} } - pub async fn find_by_collection_and_user(collection_uuid: &str, user_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_collection_and_user( + collection_uuid: &str, + user_uuid: &str, + conn: &mut DbConn, + ) -> Option { db_run! { conn: { users_collections::table .filter(users_collections::collection_uuid.eq(collection_uuid)) @@ -390,7 +398,7 @@ impl CollectionUser { }} } - pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_collections::table .filter(users_collections::user_uuid.eq(user_uuid)) @@ -401,7 +409,7 @@ impl CollectionUser { }} } - pub async fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_collection(collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { for collection in CollectionUser::find_by_collection(collection_uuid, conn).await.iter() { User::update_uuid_revision(&collection.user_uuid, conn).await; } @@ -413,7 +421,7 @@ impl CollectionUser { }} } - pub async fn delete_all_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> EmptyResult { let collectionusers = Self::find_by_organization_and_user_uuid(org_uuid, user_uuid, conn).await; db_run! { conn: { @@ -432,7 +440,7 @@ impl CollectionUser { /// Database methods impl CollectionCipher { - pub async fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn save(cipher_uuid: &str, collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { Self::update_users_revision(collection_uuid, conn).await; db_run! { conn: @@ -462,7 +470,7 @@ impl CollectionCipher { } } - pub async fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { Self::update_users_revision(collection_uuid, conn).await; db_run! { conn: { @@ -476,7 +484,7 @@ impl CollectionCipher { }} } - pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(ciphers_collections::table.filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))) .execute(conn) @@ -484,7 +492,7 @@ impl CollectionCipher { }} } - pub async fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_collection(collection_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(ciphers_collections::table.filter(ciphers_collections::collection_uuid.eq(collection_uuid))) .execute(conn) @@ -492,7 +500,7 @@ impl CollectionCipher { }} } - pub async fn update_users_revision(collection_uuid: &str, conn: &DbConn) { + pub async fn update_users_revision(collection_uuid: &str, conn: &mut 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 ce6fa638..e8a933cb 100644 --- a/src/db/models/device.rs +++ b/src/db/models/device.rs @@ -4,9 +4,9 @@ use crate::CONFIG; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "devices"] - #[changeset_options(treat_none_as_null="true")] - #[primary_key(uuid, user_uuid)] + #[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, @@ -116,7 +116,7 @@ use crate::error::MapResult; /// Database methods impl Device { - pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { self.updated_at = Utc::now().naive_utc(); db_run! { conn: @@ -136,7 +136,7 @@ impl Device { } } - pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(devices::table.filter(devices::user_uuid.eq(user_uuid))) .execute(conn) @@ -144,7 +144,7 @@ impl Device { }} } - pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { devices::table .filter(devices::uuid.eq(uuid)) @@ -155,7 +155,7 @@ impl Device { }} } - pub async fn find_by_refresh_token(refresh_token: &str, conn: &DbConn) -> Option { + pub async fn find_by_refresh_token(refresh_token: &str, conn: &mut DbConn) -> Option { db_run! { conn: { devices::table .filter(devices::refresh_token.eq(refresh_token)) @@ -165,7 +165,7 @@ impl Device { }} } - pub async fn find_latest_active_by_user(user_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_latest_active_by_user(user_uuid: &str, conn: &mut DbConn) -> Option { 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 1f0b84fd..3971fa04 100644 --- a/src/db/models/emergency_access.rs +++ b/src/db/models/emergency_access.rs @@ -5,9 +5,9 @@ use super::User; db_object! { #[derive(Debug, Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "emergency_access"] - #[changeset_options(treat_none_as_null="true")] - #[primary_key(uuid)] + #[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, @@ -72,7 +72,7 @@ impl EmergencyAccess { }) } - pub async fn to_json_grantor_details(&self, conn: &DbConn) -> Value { + pub async fn to_json_grantor_details(&self, conn: &mut DbConn) -> Value { let grantor_user = User::find_by_uuid(&self.grantor_uuid, conn).await.expect("Grantor user not found."); json!({ @@ -88,7 +88,7 @@ impl EmergencyAccess { } #[allow(clippy::manual_map)] - pub async fn to_json_grantee_details(&self, conn: &DbConn) -> Value { + pub async fn to_json_grantee_details(&self, conn: &mut 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() { @@ -154,7 +154,7 @@ use crate::api::EmptyResult; use crate::error::MapResult; impl EmergencyAccess { - pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { User::update_uuid_revision(&self.grantor_uuid, conn).await; self.updated_at = Utc::now().naive_utc(); @@ -189,7 +189,7 @@ impl EmergencyAccess { } } - pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { for ea in Self::find_all_by_grantor_uuid(user_uuid, conn).await { ea.delete(conn).await?; } @@ -199,7 +199,7 @@ impl EmergencyAccess { Ok(()) } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { User::update_uuid_revision(&self.grantor_uuid, conn).await; db_run! { conn: { @@ -209,7 +209,7 @@ impl EmergencyAccess { }} } - pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { emergency_access::table .filter(emergency_access::uuid.eq(uuid)) @@ -222,7 +222,7 @@ impl EmergencyAccess { grantor_uuid: &str, grantee_uuid: &str, email: &str, - conn: &DbConn, + conn: &mut DbConn, ) -> Option { db_run! { conn: { emergency_access::table @@ -233,7 +233,7 @@ impl EmergencyAccess { }} } - pub async fn find_all_recoveries(conn: &DbConn) -> Vec { + pub async fn find_all_recoveries(conn: &mut DbConn) -> Vec { db_run! { conn: { emergency_access::table .filter(emergency_access::status.eq(EmergencyAccessStatus::RecoveryInitiated as i32)) @@ -241,7 +241,7 @@ impl EmergencyAccess { }} } - pub async fn find_by_uuid_and_grantor_uuid(uuid: &str, grantor_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid_and_grantor_uuid(uuid: &str, grantor_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { emergency_access::table .filter(emergency_access::uuid.eq(uuid)) @@ -251,7 +251,7 @@ impl EmergencyAccess { }} } - pub async fn find_all_by_grantee_uuid(grantee_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_all_by_grantee_uuid(grantee_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { emergency_access::table .filter(emergency_access::grantee_uuid.eq(grantee_uuid)) @@ -259,7 +259,7 @@ impl EmergencyAccess { }} } - pub async fn find_invited_by_grantee_email(grantee_email: &str, conn: &DbConn) -> Option { + pub async fn find_invited_by_grantee_email(grantee_email: &str, conn: &mut DbConn) -> Option { db_run! { conn: { emergency_access::table .filter(emergency_access::email.eq(grantee_email)) @@ -269,7 +269,7 @@ impl EmergencyAccess { }} } - pub async fn find_all_by_grantor_uuid(grantor_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_all_by_grantor_uuid(grantor_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { emergency_access::table .filter(emergency_access::grantor_uuid.eq(grantor_uuid)) diff --git a/src/db/models/favorite.rs b/src/db/models/favorite.rs index fd67c60c..a301f597 100644 --- a/src/db/models/favorite.rs +++ b/src/db/models/favorite.rs @@ -2,8 +2,8 @@ use super::User; db_object! { #[derive(Identifiable, Queryable, Insertable)] - #[table_name = "favorites"] - #[primary_key(user_uuid, cipher_uuid)] + #[diesel(table_name = favorites)] + #[diesel(primary_key(user_uuid, cipher_uuid))] pub struct Favorite { pub user_uuid: String, pub cipher_uuid: String, @@ -17,7 +17,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: &DbConn) -> bool { + pub async fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> bool { db_run! { conn: { let query = favorites::table .filter(favorites::cipher_uuid.eq(cipher_uuid)) @@ -29,7 +29,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: &DbConn) -> EmptyResult { + pub async fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> EmptyResult { let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, conn).await, favorite); match (old, new) { (false, true) => { @@ -62,7 +62,7 @@ impl Favorite { } // Delete all favorite entries associated with the specified cipher. - pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid))) .execute(conn) @@ -71,7 +71,7 @@ impl Favorite { } // Delete all favorite entries associated with the specified user. - pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid))) .execute(conn) @@ -81,7 +81,7 @@ 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: &DbConn) -> Vec { + pub async fn get_all_cipher_uuid_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { favorites::table .filter(favorites::user_uuid.eq(user_uuid)) diff --git a/src/db/models/folder.rs b/src/db/models/folder.rs index 0b76704f..9385e78d 100644 --- a/src/db/models/folder.rs +++ b/src/db/models/folder.rs @@ -5,8 +5,8 @@ use super::User; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "folders"] - #[primary_key(uuid)] + #[diesel(table_name = folders)] + #[diesel(primary_key(uuid))] pub struct Folder { pub uuid: String, pub created_at: NaiveDateTime, @@ -16,8 +16,8 @@ db_object! { } #[derive(Identifiable, Queryable, Insertable)] - #[table_name = "folders_ciphers"] - #[primary_key(cipher_uuid, folder_uuid)] + #[diesel(table_name = folders_ciphers)] + #[diesel(primary_key(cipher_uuid, folder_uuid))] pub struct FolderCipher { pub cipher_uuid: String, pub folder_uuid: String, @@ -67,7 +67,7 @@ use crate::error::MapResult; /// Database methods impl Folder { - pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; self.updated_at = Utc::now().naive_utc(); @@ -102,7 +102,7 @@ impl Folder { } } - pub async fn delete(&self, conn: &DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; FolderCipher::delete_all_by_folder(&self.uuid, conn).await?; @@ -113,14 +113,14 @@ impl Folder { }} } - pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut 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: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { folders::table .filter(folders::uuid.eq(uuid)) @@ -130,7 +130,7 @@ impl Folder { }} } - pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { folders::table .filter(folders::user_uuid.eq(user_uuid)) @@ -142,7 +142,7 @@ impl Folder { } impl FolderCipher { - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: sqlite, mysql { // Not checking for ForeignKey Constraints here. @@ -164,7 +164,7 @@ impl FolderCipher { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete( folders_ciphers::table @@ -176,7 +176,7 @@ impl FolderCipher { }} } - pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_cipher(cipher_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(folders_ciphers::table.filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))) .execute(conn) @@ -184,7 +184,7 @@ impl FolderCipher { }} } - pub async fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_folder(folder_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(folders_ciphers::table.filter(folders_ciphers::folder_uuid.eq(folder_uuid))) .execute(conn) @@ -192,7 +192,7 @@ impl FolderCipher { }} } - pub async fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { folders_ciphers::table .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) @@ -203,7 +203,7 @@ impl FolderCipher { }} } - pub async fn find_by_folder(folder_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_folder(folder_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { folders_ciphers::table .filter(folders_ciphers::folder_uuid.eq(folder_uuid)) @@ -215,7 +215,7 @@ impl FolderCipher { /// 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: &DbConn) -> Vec<(String, String)> { + pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<(String, String)> { db_run! { conn: { folders_ciphers::table .inner_join(folders::table) diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 02ca8408..caa3335f 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -10,8 +10,8 @@ use super::{TwoFactor, UserOrgStatus, UserOrgType, UserOrganization}; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "org_policies"] - #[primary_key(uuid)] + #[diesel(table_name = org_policies)] + #[diesel(primary_key(uuid))] pub struct OrgPolicy { pub uuid: String, pub org_uuid: String, @@ -83,7 +83,7 @@ impl OrgPolicy { /// Database methods impl OrgPolicy { - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: sqlite, mysql { match diesel::replace_into(org_policies::table) @@ -126,7 +126,7 @@ impl OrgPolicy { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(org_policies::table.filter(org_policies::uuid.eq(self.uuid))) .execute(conn) @@ -134,7 +134,7 @@ impl OrgPolicy { }} } - pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { org_policies::table .filter(org_policies::uuid.eq(uuid)) @@ -144,7 +144,7 @@ impl OrgPolicy { }} } - pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { org_policies::table .filter(org_policies::org_uuid.eq(org_uuid)) @@ -154,7 +154,7 @@ impl OrgPolicy { }} } - pub async fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { org_policies::table .inner_join( @@ -172,7 +172,7 @@ impl OrgPolicy { }} } - pub async fn find_by_org_and_type(org_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> Option { + pub async fn find_by_org_and_type(org_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> Option { db_run! { conn: { org_policies::table .filter(org_policies::org_uuid.eq(org_uuid)) @@ -183,7 +183,7 @@ impl OrgPolicy { }} } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(org_policies::table.filter(org_policies::org_uuid.eq(org_uuid))) .execute(conn) @@ -194,7 +194,7 @@ impl OrgPolicy { pub async fn find_accepted_and_confirmed_by_user_and_active_policy( user_uuid: &str, policy_type: OrgPolicyType, - conn: &DbConn, + conn: &mut DbConn, ) -> Vec { db_run! { conn: { org_policies::table @@ -221,7 +221,7 @@ impl OrgPolicy { pub async fn find_confirmed_by_user_and_active_policy( user_uuid: &str, policy_type: OrgPolicyType, - conn: &DbConn, + conn: &mut DbConn, ) -> Vec { db_run! { conn: { org_policies::table @@ -249,7 +249,7 @@ impl OrgPolicy { user_uuid: &str, policy_type: OrgPolicyType, exclude_org_uuid: Option<&str>, - conn: &DbConn, + conn: &mut DbConn, ) -> bool { for policy in OrgPolicy::find_accepted_and_confirmed_by_user_and_active_policy(user_uuid, policy_type, conn).await @@ -272,7 +272,7 @@ impl OrgPolicy { user_uuid: &str, org_uuid: &str, exclude_current_org: bool, - conn: &DbConn, + conn: &mut DbConn, ) -> OrgPolicyResult { // Enforce TwoFactor/TwoStep login if TwoFactor::find_by_user(user_uuid, conn).await.is_empty() { @@ -300,7 +300,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: &DbConn) -> bool { + pub async fn is_hide_email_disabled(user_uuid: &str, conn: &mut 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 eb2de71a..a82dba5d 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -6,8 +6,8 @@ use super::{CollectionUser, OrgPolicy, OrgPolicyType, User}; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "organizations"] - #[primary_key(uuid)] + #[diesel(table_name = organizations)] + #[diesel(primary_key(uuid))] pub struct Organization { pub uuid: String, pub name: String, @@ -17,8 +17,8 @@ db_object! { } #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "users_organizations"] - #[primary_key(uuid)] + #[diesel(table_name = users_organizations)] + #[diesel(primary_key(uuid))] pub struct UserOrganization { pub uuid: String, pub user_uuid: String, @@ -216,7 +216,7 @@ use crate::error::MapResult; /// Database methods impl Organization { - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { for user_org in UserOrganization::find_by_org(&self.uuid, conn).await.iter() { User::update_uuid_revision(&user_org.user_uuid, conn).await; } @@ -253,7 +253,7 @@ impl Organization { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { use super::{Cipher, Collection}; Cipher::delete_all_by_organization(&self.uuid, conn).await?; @@ -268,7 +268,7 @@ impl Organization { }} } - pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { organizations::table .filter(organizations::uuid.eq(uuid)) @@ -277,7 +277,7 @@ impl Organization { }} } - pub async fn get_all(conn: &DbConn) -> Vec { + pub async fn get_all(conn: &mut DbConn) -> Vec { db_run! { conn: { organizations::table.load::(conn).expect("Error loading organizations").from_db() }} @@ -285,7 +285,7 @@ impl Organization { } impl UserOrganization { - pub async fn to_json(&self, conn: &DbConn) -> Value { + pub async fn to_json(&self, conn: &mut 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 @@ -350,7 +350,7 @@ impl UserOrganization { }) } - pub async fn to_json_user_details(&self, conn: &DbConn) -> Value { + pub async fn to_json_user_details(&self, conn: &mut 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. @@ -383,7 +383,7 @@ impl UserOrganization { }) } - pub async fn to_json_details(&self, conn: &DbConn) -> Value { + pub async fn to_json_details(&self, conn: &mut DbConn) -> Value { let coll_uuids = if self.access_all { vec![] // If we have complete access, no need to fill the array } else { @@ -421,7 +421,7 @@ impl UserOrganization { "Object": "organizationUserDetails", }) } - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await; db_run! { conn: @@ -455,7 +455,7 @@ impl UserOrganization { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut 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?; @@ -467,21 +467,21 @@ impl UserOrganization { }} } - pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut 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: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut 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: &DbConn) -> Option { + pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &mut 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); @@ -503,7 +503,7 @@ impl UserOrganization { (self.access_all || self.atype >= UserOrgType::Admin) && self.has_status(UserOrgStatus::Confirmed) } - pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { users_organizations::table .filter(users_organizations::uuid.eq(uuid)) @@ -512,7 +512,7 @@ impl UserOrganization { }} } - pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { users_organizations::table .filter(users_organizations::uuid.eq(uuid)) @@ -522,7 +522,7 @@ impl UserOrganization { }} } - pub async fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -532,7 +532,7 @@ impl UserOrganization { }} } - pub async fn find_invited_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_invited_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -542,7 +542,7 @@ impl UserOrganization { }} } - pub async fn find_any_state_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_any_state_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -551,7 +551,7 @@ impl UserOrganization { }} } - pub async fn count_accepted_and_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> i64 { + pub async fn count_accepted_and_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -563,7 +563,7 @@ impl UserOrganization { }} } - pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -572,7 +572,7 @@ impl UserOrganization { }} } - pub async fn count_by_org(org_uuid: &str, conn: &DbConn) -> i64 { + pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -583,7 +583,7 @@ impl UserOrganization { }} } - pub async fn find_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &DbConn) -> Vec { + pub async fn find_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -593,7 +593,7 @@ impl UserOrganization { }} } - pub async fn count_confirmed_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &DbConn) -> i64 { + pub async fn count_confirmed_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &mut DbConn) -> i64 { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -605,7 +605,7 @@ impl UserOrganization { }} } - pub async fn find_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -615,7 +615,7 @@ impl UserOrganization { }} } - pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -624,7 +624,7 @@ impl UserOrganization { }} } - pub async fn find_by_user_and_policy(user_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> Vec { + pub async fn find_by_user_and_policy(user_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .inner_join( @@ -643,7 +643,7 @@ impl UserOrganization { }} } - pub async fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) @@ -665,7 +665,7 @@ impl UserOrganization { }} } - pub async fn find_by_collection_and_org(collection_uuid: &str, org_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_collection_and_org(collection_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) diff --git a/src/db/models/send.rs b/src/db/models/send.rs index 687571d5..effc5dfc 100644 --- a/src/db/models/send.rs +++ b/src/db/models/send.rs @@ -5,9 +5,9 @@ use super::User; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "sends"] - #[changeset_options(treat_none_as_null="true")] - #[primary_key(uuid)] + #[diesel(table_name = sends)] + #[diesel(treat_none_as_null = true)] + #[diesel(primary_key(uuid))] pub struct Send { pub uuid: String, @@ -101,7 +101,7 @@ impl Send { } } - pub async fn creator_identifier(&self, conn: &DbConn) -> Option { + pub async fn creator_identifier(&self, conn: &mut DbConn) -> Option { if let Some(hide_email) = self.hide_email { if hide_email { return None; @@ -148,7 +148,7 @@ impl Send { }) } - pub async fn to_json_access(&self, conn: &DbConn) -> Value { + pub async fn to_json_access(&self, conn: &mut DbConn) -> Value { use crate::util::format_date; let data: Value = serde_json::from_str(&self.data).unwrap_or_default(); @@ -174,7 +174,7 @@ use crate::api::EmptyResult; use crate::error::MapResult; impl Send { - pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { self.update_users_revision(conn).await; self.revision_date = Utc::now().naive_utc(); @@ -209,7 +209,7 @@ impl Send { } } - pub async fn delete(&self, conn: &DbConn) -> EmptyResult { + pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { self.update_users_revision(conn).await; if self.atype == SendType::File as i32 { @@ -224,13 +224,13 @@ impl Send { } /// Purge all sends that are past their deletion date. - pub async fn purge(conn: &DbConn) { + pub async fn purge(conn: &mut 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: &DbConn) -> Vec { + pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec { let mut user_uuids = Vec::new(); match &self.user_uuid { Some(user_uuid) => { @@ -244,14 +244,14 @@ impl Send { user_uuids } - pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut 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: &DbConn) -> Option { + pub async fn find_by_access_id(access_id: &str, conn: &mut DbConn) -> Option { use data_encoding::BASE64URL_NOPAD; use uuid::Uuid; @@ -268,7 +268,7 @@ impl Send { Self::find_by_uuid(&uuid, conn).await } - pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! {conn: { sends::table .filter(sends::uuid.eq(uuid)) @@ -278,7 +278,7 @@ impl Send { }} } - pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! {conn: { sends::table .filter(sends::user_uuid.eq(user_uuid)) @@ -286,7 +286,7 @@ impl Send { }} } - pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec { db_run! {conn: { sends::table .filter(sends::organization_uuid.eq(org_uuid)) @@ -294,7 +294,7 @@ impl Send { }} } - pub async fn find_by_past_deletion_date(conn: &DbConn) -> Vec { + pub async fn find_by_past_deletion_date(conn: &mut DbConn) -> Vec { let now = Utc::now().naive_utc(); db_run! {conn: { sends::table diff --git a/src/db/models/two_factor.rs b/src/db/models/two_factor.rs index 56d7e1e7..ef03979a 100644 --- a/src/db/models/two_factor.rs +++ b/src/db/models/two_factor.rs @@ -4,8 +4,8 @@ use crate::{api::EmptyResult, db::DbConn, error::MapResult}; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "twofactor"] - #[primary_key(uuid)] + #[diesel(table_name = twofactor)] + #[diesel(primary_key(uuid))] pub struct TwoFactor { pub uuid: String, pub user_uuid: String, @@ -68,7 +68,7 @@ impl TwoFactor { /// Database methods impl TwoFactor { - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: sqlite, mysql { match diesel::replace_into(twofactor::table) @@ -107,7 +107,7 @@ impl TwoFactor { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(twofactor::table.filter(twofactor::uuid.eq(self.uuid))) .execute(conn) @@ -115,7 +115,7 @@ impl TwoFactor { }} } - pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { twofactor::table .filter(twofactor::user_uuid.eq(user_uuid)) @@ -126,7 +126,7 @@ impl TwoFactor { }} } - pub async fn find_by_user_and_type(user_uuid: &str, atype: i32, conn: &DbConn) -> Option { + pub async fn find_by_user_and_type(user_uuid: &str, atype: i32, conn: &mut DbConn) -> Option { db_run! { conn: { twofactor::table .filter(twofactor::user_uuid.eq(user_uuid)) @@ -137,7 +137,7 @@ impl TwoFactor { }} } - pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid))) .execute(conn) @@ -145,7 +145,7 @@ impl TwoFactor { }} } - pub async fn migrate_u2f_to_webauthn(conn: &DbConn) -> EmptyResult { + pub async fn migrate_u2f_to_webauthn(conn: &mut DbConn) -> EmptyResult { let u2f_factors = db_run! { conn: { twofactor::table .filter(twofactor::atype.eq(TwoFactorType::U2f as i32)) diff --git a/src/db/models/two_factor_incomplete.rs b/src/db/models/two_factor_incomplete.rs index 7f3021b4..49f7691f 100644 --- a/src/db/models/two_factor_incomplete.rs +++ b/src/db/models/two_factor_incomplete.rs @@ -4,8 +4,8 @@ use crate::{api::EmptyResult, auth::ClientIp, db::DbConn, error::MapResult, CONF db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "twofactor_incomplete"] - #[primary_key(user_uuid, device_uuid)] + #[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 @@ -24,7 +24,7 @@ impl TwoFactorIncomplete { device_uuid: &str, device_name: &str, ip: &ClientIp, - conn: &DbConn, + conn: &mut DbConn, ) -> EmptyResult { if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() { return Ok(()); @@ -52,7 +52,7 @@ impl TwoFactorIncomplete { }} } - pub async fn mark_complete(user_uuid: &str, device_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn mark_complete(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> EmptyResult { if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() { return Ok(()); } @@ -60,7 +60,7 @@ 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: &DbConn) -> Option { + pub async fn find_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { twofactor_incomplete::table .filter(twofactor_incomplete::user_uuid.eq(user_uuid)) @@ -71,7 +71,7 @@ impl TwoFactorIncomplete { }} } - pub async fn find_logins_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec { + pub async fn find_logins_before(dt: &NaiveDateTime, conn: &mut DbConn) -> Vec { db_run! {conn: { twofactor_incomplete::table .filter(twofactor_incomplete::login_time.lt(dt)) @@ -81,11 +81,11 @@ impl TwoFactorIncomplete { }} } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut 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: &DbConn) -> EmptyResult { + pub async fn delete_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> EmptyResult { db_run! { conn: { diesel::delete(twofactor_incomplete::table .filter(twofactor_incomplete::user_uuid.eq(user_uuid)) @@ -95,7 +95,7 @@ impl TwoFactorIncomplete { }} } - pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { + pub async fn delete_all_by_user(user_uuid: &str, conn: &mut 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 9e692a3f..826e00fa 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -6,9 +6,9 @@ use crate::CONFIG; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] - #[table_name = "users"] - #[changeset_options(treat_none_as_null="true")] - #[primary_key(uuid)] + #[diesel(table_name = users)] + #[diesel(treat_none_as_null = true)] + #[diesel(primary_key(uuid))] pub struct User { pub uuid: String, pub enabled: bool, @@ -32,7 +32,7 @@ db_object! { pub private_key: Option, pub public_key: Option, - #[column_name = "totp_secret"] // Note, this is only added to the UserDb structs, not to User + #[diesel(column_name = "totp_secret")] // Note, this is only added to the UserDb structs, not to User _totp_secret: Option, pub totp_recover: Option, @@ -49,8 +49,8 @@ db_object! { } #[derive(Identifiable, Queryable, Insertable)] - #[table_name = "invitations"] - #[primary_key(email)] + #[diesel(table_name = invitations)] + #[diesel(primary_key(email))] pub struct Invitation { pub email: String, } @@ -192,18 +192,13 @@ use crate::db::DbConn; use crate::api::EmptyResult; use crate::error::MapResult; -use futures::{stream, stream::StreamExt}; - /// Database methods impl User { - pub async fn to_json(&self, conn: &DbConn) -> Value { - let orgs_json = stream::iter(UserOrganization::find_confirmed_by_user(&self.uuid, conn).await) - .then(|c| async { - let c = c; // Move out this single variable - c.to_json(conn).await - }) - .collect::>() - .await; + pub async fn to_json(&self, conn: &mut 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); + } let twofactor_enabled = !TwoFactor::find_by_user(&self.uuid, conn).await.is_empty(); @@ -235,7 +230,7 @@ impl User { }) } - pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &mut DbConn) -> EmptyResult { if self.email.trim().is_empty() { err!("User email can't be empty") } @@ -273,7 +268,7 @@ impl User { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut 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 @@ -301,13 +296,13 @@ impl User { }} } - pub async fn update_uuid_revision(uuid: &str, conn: &DbConn) { + pub async fn update_uuid_revision(uuid: &str, conn: &mut 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: &DbConn) -> EmptyResult { + pub async fn update_all_revisions(conn: &mut DbConn) -> EmptyResult { let updated_at = Utc::now().naive_utc(); db_run! {conn: { @@ -320,13 +315,13 @@ impl User { }} } - pub async fn update_revision(&mut self, conn: &DbConn) -> EmptyResult { + pub async fn update_revision(&mut self, conn: &mut 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: &DbConn) -> EmptyResult { + async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &mut DbConn) -> EmptyResult { db_run! {conn: { crate::util::retry(|| { diesel::update(users::table.filter(users::uuid.eq(uuid))) @@ -337,7 +332,7 @@ impl User { }} } - pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option { + pub async fn find_by_mail(mail: &str, conn: &mut DbConn) -> Option { let lower_mail = mail.to_lowercase(); db_run! {conn: { users::table @@ -348,19 +343,19 @@ impl User { }} } - pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option { + pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! {conn: { users::table.filter(users::uuid.eq(uuid)).first::(conn).ok().from_db() }} } - pub async fn get_all(conn: &DbConn) -> Vec { + pub async fn get_all(conn: &mut DbConn) -> Vec { db_run! {conn: { users::table.load::(conn).expect("Error loading users").from_db() }} } - pub async fn last_active(&self, conn: &DbConn) -> Option { + pub async fn last_active(&self, conn: &mut DbConn) -> Option { match Device::find_latest_active_by_user(&self.uuid, conn).await { Some(device) => Some(device.updated_at), None => None, @@ -376,7 +371,7 @@ impl Invitation { } } - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { if self.email.trim().is_empty() { err!("Invitation email can't be empty") } @@ -401,7 +396,7 @@ impl Invitation { } } - pub async fn delete(self, conn: &DbConn) -> EmptyResult { + pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { db_run! {conn: { diesel::delete(invitations::table.filter(invitations::email.eq(self.email))) .execute(conn) @@ -409,7 +404,7 @@ impl Invitation { }} } - pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option { + pub async fn find_by_mail(mail: &str, conn: &mut DbConn) -> Option { let lower_mail = mail.to_lowercase(); db_run! {conn: { invitations::table @@ -420,7 +415,7 @@ impl Invitation { }} } - pub async fn take(mail: &str, conn: &DbConn) -> bool { + pub async fn take(mail: &str, conn: &mut DbConn) -> bool { match Self::find_by_mail(mail, conn).await { Some(invitation) => invitation.delete(conn).await.is_ok(), None => false, diff --git a/src/error.rs b/src/error.rs index fe42a293..d42ecd20 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,7 +36,6 @@ macro_rules! make_error { use diesel::r2d2::PoolError as R2d2Err; use diesel::result::Error as DieselErr; use diesel::ConnectionError as DieselConErr; -use diesel_migrations::RunMigrationsError as DieselMigErr; use handlebars::RenderError as HbErr; use jsonwebtoken::errors::Error as JwtErr; use lettre::address::AddressError as AddrErr; @@ -87,7 +86,6 @@ make_error! { Rocket(RocketErr): _has_source, _api_error, DieselCon(DieselConErr): _has_source, _api_error, - DieselMig(DieselMigErr): _has_source, _api_error, Webauthn(WebauthnErr): _has_source, _api_error, WebSocket(TungstError): _has_source, _api_error, } diff --git a/src/main.rs b/src/main.rs index ad47f3c5..9a391ac3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ // The more key/value pairs there are the more recursion occurs. // We want to keep this as low as possible, but not higher then 128. // If you go above 128 it will cause rust-analyzer to fail, -#![recursion_limit = "87"] +#![recursion_limit = "94"] // When enabled use MiMalloc as malloc instead of the default malloc #[cfg(feature = "enable_mimalloc")] @@ -108,7 +108,7 @@ async fn main() -> Result<(), Error> { let pool = create_db_pool().await; schedule_jobs(pool.clone()).await; - crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&pool.get().await.unwrap()).await.unwrap(); + crate::db::models::TwoFactor::migrate_u2f_to_webauthn(&mut pool.get().await.unwrap()).await.unwrap(); launch_rocket(pool, extra_debug).await // Blocks until program termination. } diff --git a/src/util.rs b/src/util.rs index ca3667e1..4599a89a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -587,9 +587,9 @@ fn _process_key(key: &str) -> String { // Retry methods // -pub fn retry(func: F, max_tries: u32) -> Result +pub fn retry(mut func: F, max_tries: u32) -> Result where - F: Fn() -> Result, + F: FnMut() -> Result, { let mut tries = 0; @@ -608,9 +608,9 @@ where } } -pub async fn retry_db(func: F, max_tries: u32) -> Result +pub async fn retry_db(mut func: F, max_tries: u32) -> Result where - F: Fn() -> Result, + F: FnMut() -> Result, E: std::error::Error, { let mut tries = 0;