vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

commit 729b5631601f7b7f857ae2b9c0eceddbac8a1d15
parent 6b5618a5fc0abade1a23d084c87f93e6c1c8f2f8
Author: Daniel GarcĂ­a <dani-garcia@users.noreply.github.com>
Date:   Wed, 15 Mar 2023 22:28:03 +0100

Merge pull request #3332 from BlackDex/merge-clientip-with-headers

Merge ClientIp with Headers.
Diffstat:
Msrc/api/admin.rs | 37+++++++++++++++++++------------------
Msrc/api/core/accounts.rs | 16+++++-----------
Msrc/api/core/ciphers.rs | 228+++++++++++++++++++++++++------------------------------------------------------
Msrc/api/core/events.rs | 15+++++----------
Msrc/api/core/organizations.rs | 173++++++++++++++++++++++++++-----------------------------------------------------
Msrc/api/core/two_factor/authenticator.rs | 8+++-----
Msrc/api/core/two_factor/duo.rs | 10+++++-----
Msrc/api/core/two_factor/email.rs | 8++++----
Msrc/api/core/two_factor/mod.rs | 39++++++++++++++++-----------------------
Msrc/api/core/two_factor/webauthn.rs | 20+++++---------------
Msrc/api/core/two_factor/yubikey.rs | 22++++++----------------
Msrc/api/identity.rs | 17++++++++++++-----
Msrc/api/notifications.rs | 29++---------------------------
Msrc/auth.rs | 25+++++++++++++++++++++++++
Msrc/db/models/cipher.rs | 7++++---
Msrc/main.rs | 17+++++++++++++----
Msrc/util.rs | 7+++----
17 files changed, 256 insertions(+), 422 deletions(-)

diff --git a/src/api/admin.rs b/src/api/admin.rs @@ -369,7 +369,7 @@ async fn get_user_json(uuid: String, _token: AdminToken, mut conn: DbConn) -> Js } #[post("/users/<uuid>/delete")] -async fn delete_user(uuid: String, _token: AdminToken, mut conn: DbConn, ip: ClientIp) -> EmptyResult { +async fn delete_user(uuid: String, token: AdminToken, mut conn: DbConn) -> EmptyResult { let user = get_user_or_404(&uuid, &mut conn).await?; // Get the user_org records before deleting the actual user @@ -383,7 +383,7 @@ async fn delete_user(uuid: String, _token: AdminToken, mut conn: DbConn, ip: Cli user_org.org_uuid, String::from(ACTING_ADMIN_USER), 14, // Use UnknownBrowser type - &ip.ip, + &token.ip.ip, &mut conn, ) .await; @@ -443,12 +443,7 @@ struct UserOrgTypeData { } #[post("/users/org_type", data = "<data>")] -async fn update_user_org_type( - data: Json<UserOrgTypeData>, - _token: AdminToken, - mut conn: DbConn, - ip: ClientIp, -) -> EmptyResult { +async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult { let data: UserOrgTypeData = data.into_inner(); let mut user_to_edit = @@ -489,7 +484,7 @@ async fn update_user_org_type( data.org_uuid, String::from(ACTING_ADMIN_USER), 14, // Use UnknownBrowser type - &ip.ip, + &token.ip.ip, &mut conn, ) .await; @@ -724,15 +719,24 @@ async fn backup_db(_token: AdminToken, mut conn: DbConn) -> EmptyResult { } } -pub struct AdminToken {} +pub struct AdminToken { + ip: ClientIp, +} #[rocket::async_trait] impl<'r> FromRequest<'r> for AdminToken { type Error = &'static str; async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { + let ip = match ClientIp::from_request(request).await { + Outcome::Success(ip) => ip, + _ => err_handler!("Error getting Client IP"), + }; + if CONFIG.disable_admin_token() { - Outcome::Success(Self {}) + Outcome::Success(Self { + ip, + }) } else { let cookies = request.cookies(); @@ -741,19 +745,16 @@ impl<'r> FromRequest<'r> for AdminToken { None => return Outcome::Failure((Status::Unauthorized, "Unauthorized")), }; - let ip = match ClientIp::from_request(request).await { - Outcome::Success(ip) => ip.ip, - _ => err_handler!("Error getting Client IP"), - }; - if decode_admin(access_token).is_err() { // Remove admin cookie cookies.remove(Cookie::build(COOKIE_NAME, "").path(admin_path()).finish()); - error!("Invalid or expired admin JWT. IP: {}.", ip); + error!("Invalid or expired admin JWT. IP: {}.", &ip.ip); return Outcome::Failure((Status::Unauthorized, "Session expired")); } - Outcome::Success(Self {}) + Outcome::Success(Self { + ip, + }) } } } diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs @@ -6,7 +6,7 @@ use crate::{ api::{ core::log_user_event, EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType, }, - auth::{decode_delete, decode_invite, decode_verify_email, ClientIp, Headers}, + auth::{decode_delete, decode_invite, decode_verify_email, Headers}, crypto, db::{models::*, DbConn}, mail, CONFIG, @@ -311,7 +311,6 @@ async fn post_password( data: JsonUpcase<ChangePassData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data: ChangePassData = data.into_inner().data; @@ -324,7 +323,8 @@ async fn post_password( user.password_hint = clean_password_hint(&data.MasterPasswordHint); enforce_password_hint_setting(&user.password_hint)?; - log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; + log_user_event(EventType::UserChangedPassword as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn) + .await; user.set_password( &data.NewMasterPasswordHash, @@ -420,13 +420,7 @@ struct KeyData { } #[post("/accounts/key", data = "<data>")] -async fn post_rotatekey( - data: JsonUpcase<KeyData>, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> EmptyResult { +async fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { let data: KeyData = data.into_inner().data; if !headers.user.check_valid_password(&data.MasterPasswordHash) { @@ -472,7 +466,7 @@ async fn post_rotatekey( // Prevent triggering cipher updates via WebSockets by settings UpdateType::None // The user sessions are invalidated because all the ciphers were re-encrypted and thus triggering an update could cause issues. // We force the users to logout after the user has been saved to try and prevent these issues. - update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None) + update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None) .await? } diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs @@ -11,7 +11,7 @@ use serde_json::Value; use crate::{ api::{self, core::log_event, EmptyResult, JsonResult, JsonUpcase, Notify, PasswordData, UpdateType}, - auth::{ClientIp, Headers}, + auth::Headers, crypto, db::{models::*, DbConn, DbPool}, CONFIG, @@ -263,10 +263,9 @@ async fn post_ciphers_admin( data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - post_ciphers_create(data, headers, conn, ip, nt).await + post_ciphers_create(data, headers, conn, nt).await } /// Called when creating a new org-owned cipher, or cloning a cipher (whether @@ -277,7 +276,6 @@ async fn post_ciphers_create( data: JsonUpcase<ShareCipherData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { let mut data: ShareCipherData = data.into_inner().data; @@ -305,18 +303,12 @@ async fn post_ciphers_create( // or otherwise), we can just ignore this field entirely. data.Cipher.LastKnownRevisionDate = None; - share_cipher_by_uuid(&cipher.uuid, data, &headers, &mut conn, &ip, &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 = "<data>")] -async fn post_ciphers( - data: JsonUpcase<CipherData>, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> JsonResult { +async fn post_ciphers(data: JsonUpcase<CipherData>, 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 @@ -326,8 +318,7 @@ async fn post_ciphers( data.LastKnownRevisionDate = None; let mut cipher = Cipher::new(data.Type, data.Name.clone()); - update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherCreate) - .await?; + update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::SyncCipherCreate).await?; Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) } @@ -354,14 +345,12 @@ async fn enforce_personal_ownership_policy( Ok(()) } -#[allow(clippy::too_many_arguments)] pub async fn update_cipher_from_data( cipher: &mut Cipher, data: CipherData, headers: &Headers, shared_to_collection: bool, conn: &mut DbConn, - ip: &ClientIp, nt: &Notify<'_>, ut: UpdateType, ) -> EmptyResult { @@ -517,7 +506,7 @@ pub async fn update_cipher_from_data( String::from(org_uuid), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, conn, ) .await; @@ -551,7 +540,6 @@ async fn post_ciphers_import( data: JsonUpcase<ImportData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { enforce_personal_ownership_policy(None, &headers, &mut conn).await?; @@ -586,8 +574,7 @@ async fn post_ciphers_import( cipher_data.FolderId = folder_uuid; let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); - update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &ip, &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; @@ -603,10 +590,9 @@ async fn put_cipher_admin( data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - put_cipher(uuid, data, headers, conn, ip, nt).await + put_cipher(uuid, data, headers, conn, nt).await } #[post("/ciphers/<uuid>/admin", data = "<data>")] @@ -615,10 +601,9 @@ async fn post_cipher_admin( data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - post_cipher(uuid, data, headers, conn, ip, nt).await + post_cipher(uuid, data, headers, conn, nt).await } #[post("/ciphers/<uuid>", data = "<data>")] @@ -627,10 +612,9 @@ async fn post_cipher( data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - put_cipher(uuid, data, headers, conn, ip, nt).await + put_cipher(uuid, data, headers, conn, nt).await } #[put("/ciphers/<uuid>", data = "<data>")] @@ -639,7 +623,6 @@ async fn put_cipher( data: JsonUpcase<CipherData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { let data: CipherData = data.into_inner().data; @@ -658,8 +641,7 @@ async fn put_cipher( err!("Cipher is not write accessible") } - update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherUpdate) - .await?; + update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &nt, UpdateType::SyncCipherUpdate).await?; Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await)) } @@ -720,9 +702,8 @@ async fn put_collections_update( data: JsonUpcase<CollectionsAdminData>, headers: Headers, conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - post_collections_admin(uuid, data, headers, conn, ip).await + post_collections_admin(uuid, data, headers, conn).await } #[post("/ciphers/<uuid>/collections", data = "<data>")] @@ -731,9 +712,8 @@ async fn post_collections_update( data: JsonUpcase<CollectionsAdminData>, headers: Headers, conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - post_collections_admin(uuid, data, headers, conn, ip).await + post_collections_admin(uuid, data, headers, conn).await } #[put("/ciphers/<uuid>/collections-admin", data = "<data>")] @@ -742,9 +722,8 @@ async fn put_collections_admin( data: JsonUpcase<CollectionsAdminData>, headers: Headers, conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - post_collections_admin(uuid, data, headers, conn, ip).await + post_collections_admin(uuid, data, headers, conn).await } #[post("/ciphers/<uuid>/collections-admin", data = "<data>")] @@ -753,7 +732,6 @@ async fn post_collections_admin( data: JsonUpcase<CollectionsAdminData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { let data: CollectionsAdminData = data.into_inner().data; @@ -795,7 +773,7 @@ async fn post_collections_admin( cipher.organization_uuid.unwrap(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -816,12 +794,11 @@ async fn post_cipher_share( data: JsonUpcase<ShareCipherData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { let data: ShareCipherData = data.into_inner().data; - share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &ip, &nt).await + share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await } #[put("/ciphers/<uuid>/share", data = "<data>")] @@ -830,12 +807,11 @@ async fn put_cipher_share( data: JsonUpcase<ShareCipherData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { let data: ShareCipherData = data.into_inner().data; - share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &ip, &nt).await + share_cipher_by_uuid(&uuid, data, &headers, &mut conn, &nt).await } #[derive(Deserialize)] @@ -850,7 +826,6 @@ async fn put_cipher_share_selected( data: JsonUpcase<ShareSelectedCipherData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let mut data: ShareSelectedCipherData = data.into_inner().data; @@ -878,7 +853,7 @@ async fn put_cipher_share_selected( }; match shared_cipher_data.Cipher.Id.take() { - Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &ip, &nt).await?, + Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &mut conn, &nt).await?, None => err!("Request missing ids field"), }; } @@ -891,7 +866,6 @@ async fn share_cipher_by_uuid( data: ShareCipherData, headers: &Headers, conn: &mut DbConn, - ip: &ClientIp, nt: &Notify<'_>, ) -> JsonResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { @@ -930,7 +904,7 @@ async fn share_cipher_by_uuid( UpdateType::SyncCipherCreate }; - update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, ip, nt, ut).await?; + update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, nt, ut).await?; Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, conn).await)) } @@ -1025,7 +999,6 @@ async fn save_attachment( data: Form<UploadData<'_>>, headers: &Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> Result<(Cipher, DbConn), crate::error::Error> { let cipher = match Cipher::find_by_uuid(&cipher_uuid, &mut conn).await { @@ -1144,7 +1117,7 @@ async fn save_attachment( String::from(org_uuid), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1164,7 +1137,6 @@ async fn post_attachment_v2_data( data: Form<UploadData<'_>>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let attachment = match Attachment::find_by_id(&attachment_id, &mut conn).await { @@ -1173,7 +1145,7 @@ async fn post_attachment_v2_data( None => err!("Attachment doesn't exist"), }; - save_attachment(attachment, uuid, data, &headers, conn, ip, nt).await?; + save_attachment(attachment, uuid, data, &headers, conn, nt).await?; Ok(()) } @@ -1185,14 +1157,13 @@ async fn post_attachment( data: Form<UploadData<'_>>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { // Setting this as None signifies to save_attachment() that it should create // the attachment database record as well as saving the data to disk. let attachment = None; - let (cipher, mut conn) = save_attachment(attachment, uuid, data, &headers, conn, ip, 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, CipherSyncType::User, &mut conn).await)) } @@ -1203,10 +1174,9 @@ async fn post_attachment_admin( data: Form<UploadData<'_>>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - post_attachment(uuid, data, headers, conn, ip, nt).await + post_attachment(uuid, data, headers, conn, nt).await } #[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")] @@ -1216,11 +1186,10 @@ async fn post_attachment_share( data: Form<UploadData<'_>>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await?; - post_attachment(uuid, data, headers, conn, ip, nt).await + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await?; + post_attachment(uuid, data, headers, conn, nt).await } #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")] @@ -1229,10 +1198,9 @@ async fn delete_attachment_post_admin( attachment_id: String, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - delete_attachment(uuid, attachment_id, headers, conn, ip, nt).await + delete_attachment(uuid, attachment_id, headers, conn, nt).await } #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete")] @@ -1241,10 +1209,9 @@ async fn delete_attachment_post( attachment_id: String, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - delete_attachment(uuid, attachment_id, headers, conn, ip, nt).await + delete_attachment(uuid, attachment_id, headers, conn, nt).await } #[delete("/ciphers/<uuid>/attachment/<attachment_id>")] @@ -1253,10 +1220,9 @@ async fn delete_attachment( attachment_id: String, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await } #[delete("/ciphers/<uuid>/attachment/<attachment_id>/admin")] @@ -1265,70 +1231,44 @@ async fn delete_attachment_admin( attachment_id: String, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &ip, &nt).await + _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &mut conn, &nt).await } #[post("/ciphers/<uuid>/delete")] -async fn delete_cipher_post( - uuid: String, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete +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 + // permanent delete } #[post("/ciphers/<uuid>/delete-admin")] -async fn delete_cipher_post_admin( - uuid: String, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete +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 + // permanent delete } #[put("/ciphers/<uuid>/delete")] -async fn delete_cipher_put( - uuid: String, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &ip, &nt).await // soft delete +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 + // soft delete } #[put("/ciphers/<uuid>/delete-admin")] -async fn delete_cipher_put_admin( - uuid: String, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, true, &ip, &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/<uuid>")] -async fn delete_cipher(uuid: String, headers: Headers, mut conn: DbConn, ip: ClientIp, nt: Notify<'_>) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete +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 + // permanent delete } #[delete("/ciphers/<uuid>/admin")] -async fn delete_cipher_admin( - uuid: String, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> EmptyResult { - _delete_cipher_by_uuid(&uuid, &headers, &mut conn, false, &ip, &nt).await // permanent delete +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 + // permanent delete } #[delete("/ciphers", data = "<data>")] @@ -1336,10 +1276,9 @@ async fn delete_cipher_selected( data: JsonUpcase<Value>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete + _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete } #[post("/ciphers/delete", data = "<data>")] @@ -1347,10 +1286,9 @@ async fn delete_cipher_selected_post( data: JsonUpcase<Value>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete + _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete } #[put("/ciphers/delete", data = "<data>")] @@ -1358,10 +1296,9 @@ async fn delete_cipher_selected_put( data: JsonUpcase<Value>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, true, ip, nt).await // soft delete + _delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete } #[delete("/ciphers/admin", data = "<data>")] @@ -1369,10 +1306,9 @@ async fn delete_cipher_selected_admin( data: JsonUpcase<Value>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete + _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete } #[post("/ciphers/delete-admin", data = "<data>")] @@ -1380,10 +1316,9 @@ async fn delete_cipher_selected_post_admin( data: JsonUpcase<Value>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, false, ip, nt).await // permanent delete + _delete_multiple_ciphers(data, headers, conn, false, nt).await // permanent delete } #[put("/ciphers/delete-admin", data = "<data>")] @@ -1391,32 +1326,19 @@ async fn delete_cipher_selected_put_admin( data: JsonUpcase<Value>, headers: Headers, conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_multiple_ciphers(data, headers, conn, true, ip, nt).await // soft delete + _delete_multiple_ciphers(data, headers, conn, true, nt).await // soft delete } #[put("/ciphers/<uuid>/restore")] -async fn restore_cipher_put( - uuid: String, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> JsonResult { - _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &ip, &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/<uuid>/restore-admin")] -async fn restore_cipher_put_admin( - uuid: String, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, - nt: Notify<'_>, -) -> JsonResult { - _restore_cipher_by_uuid(&uuid, &headers, &mut conn, &ip, &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 = "<data>")] @@ -1424,10 +1346,9 @@ async fn restore_cipher_selected( data: JsonUpcase<Value>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { - _restore_multiple_ciphers(data, &headers, &mut conn, ip, &nt).await + _restore_multiple_ciphers(data, &headers, &mut conn, &nt).await } #[derive(Deserialize)] @@ -1499,7 +1420,6 @@ async fn delete_all( data: JsonUpcase<PasswordData>, headers: Headers, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data: PasswordData = data.into_inner().data; @@ -1527,7 +1447,7 @@ async fn delete_all( org_data.org_id.clone(), user.uuid, headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1563,7 +1483,6 @@ async fn _delete_cipher_by_uuid( headers: &Headers, conn: &mut DbConn, soft_delete: bool, - ip: &ClientIp, nt: &Notify<'_>, ) -> EmptyResult { let mut cipher = match Cipher::find_by_uuid(uuid, conn).await { @@ -1602,8 +1521,16 @@ async fn _delete_cipher_by_uuid( false => EventType::CipherDeleted as i32, }; - log_event(event_type, &cipher.uuid, org_uuid, headers.user.uuid.clone(), headers.device.atype, &ip.ip, conn) - .await; + log_event( + event_type, + &cipher.uuid, + org_uuid, + headers.user.uuid.clone(), + headers.device.atype, + &headers.ip.ip, + conn, + ) + .await; } Ok(()) @@ -1614,7 +1541,6 @@ async fn _delete_multiple_ciphers( headers: Headers, mut conn: DbConn, soft_delete: bool, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data: Value = data.into_inner().data; @@ -1628,7 +1554,7 @@ async fn _delete_multiple_ciphers( }; for uuid in uuids { - if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &mut conn, soft_delete, &ip, &nt).await { + if let error @ Err(_) = _delete_cipher_by_uuid(uuid, &headers, &mut conn, soft_delete, &nt).await { return error; }; } @@ -1636,13 +1562,7 @@ async fn _delete_multiple_ciphers( Ok(()) } -async fn _restore_cipher_by_uuid( - uuid: &str, - headers: &Headers, - conn: &mut DbConn, - ip: &ClientIp, - 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"), @@ -1669,7 +1589,7 @@ async fn _restore_cipher_by_uuid( String::from(org_uuid), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, conn, ) .await; @@ -1682,7 +1602,6 @@ async fn _restore_multiple_ciphers( data: JsonUpcase<Value>, headers: &Headers, conn: &mut DbConn, - ip: ClientIp, nt: &Notify<'_>, ) -> JsonResult { let data: Value = data.into_inner().data; @@ -1697,7 +1616,7 @@ async fn _restore_multiple_ciphers( let mut ciphers: Vec<Value> = Vec::new(); for uuid in uuids { - match _restore_cipher_by_uuid(uuid, headers, conn, &ip, nt).await { + match _restore_cipher_by_uuid(uuid, headers, conn, nt).await { Ok(json) => ciphers.push(json.into_inner()), err => return err, } @@ -1715,7 +1634,6 @@ async fn _delete_cipher_attachment_by_id( attachment_id: &str, headers: &Headers, conn: &mut DbConn, - ip: &ClientIp, nt: &Notify<'_>, ) -> EmptyResult { let attachment = match Attachment::find_by_id(attachment_id, conn).await { @@ -1752,7 +1670,7 @@ async fn _delete_cipher_attachment_by_id( org_uuid, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, conn, ) .await; diff --git a/src/api/core/events.rs b/src/api/core/events.rs @@ -6,7 +6,7 @@ use serde_json::Value; use crate::{ api::{EmptyResult, JsonResult, JsonUpcaseVec}, - auth::{AdminHeaders, ClientIp, Headers}, + auth::{AdminHeaders, Headers}, db::{ models::{Cipher, Event, UserOrganization}, DbConn, DbPool, @@ -161,12 +161,7 @@ struct EventCollection { // https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Events/Controllers/CollectController.cs // https://github.com/bitwarden/server/blob/8a22c0479e987e756ce7412c48a732f9002f0a2d/src/Core/Services/Implementations/EventService.cs #[post("/collect", format = "application/json", data = "<data>")] -async fn post_events_collect( - data: JsonUpcaseVec<EventCollection>, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, -) -> EmptyResult { +async fn post_events_collect(data: JsonUpcaseVec<EventCollection>, headers: Headers, mut conn: DbConn) -> EmptyResult { if !CONFIG.org_events_enabled() { return Ok(()); } @@ -180,7 +175,7 @@ async fn post_events_collect( &headers.user.uuid, headers.device.atype, Some(event_date), - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -194,7 +189,7 @@ async fn post_events_collect( &headers.user.uuid, headers.device.atype, Some(event_date), - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -211,7 +206,7 @@ async fn post_events_collect( &headers.user.uuid, headers.device.atype, Some(event_date), - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs @@ -9,7 +9,7 @@ use crate::{ ApiResult, EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, JsonVec, Notify, NumberOrString, PasswordData, UpdateType, }, - auth::{decode_invite, AdminHeaders, ClientIp, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, + auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, db::{models::*, DbConn}, error::Error, mail, @@ -209,7 +209,7 @@ async fn post_delete_organization( } #[post("/organizations/<org_id>/leave")] -async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn, ip: ClientIp) -> EmptyResult { +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) => { @@ -225,7 +225,7 @@ async fn leave_organization(org_id: String, headers: Headers, mut conn: DbConn, org_id, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -249,9 +249,8 @@ async fn put_organization( headers: OwnerHeaders, data: JsonUpcase<OrganizationUpdateData>, conn: DbConn, - ip: ClientIp, ) -> JsonResult { - post_organization(org_id, headers, data, conn, ip).await + post_organization(org_id, headers, data, conn).await } #[post("/organizations/<org_id>", data = "<data>")] @@ -260,7 +259,6 @@ async fn post_organization( headers: OwnerHeaders, data: JsonUpcase<OrganizationUpdateData>, mut conn: DbConn, - ip: ClientIp, ) -> JsonResult { let data: OrganizationUpdateData = data.into_inner().data; @@ -280,7 +278,7 @@ async fn post_organization( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -380,7 +378,6 @@ async fn post_organization_collections( headers: ManagerHeadersLoose, data: JsonUpcase<NewCollectionData>, mut conn: DbConn, - ip: ClientIp, ) -> JsonResult { let data: NewCollectionData = data.into_inner().data; @@ -398,7 +395,7 @@ async fn post_organization_collections( org_id, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -433,9 +430,8 @@ async fn put_organization_collection_update( headers: ManagerHeaders, data: JsonUpcase<NewCollectionData>, conn: DbConn, - ip: ClientIp, ) -> JsonResult { - post_organization_collection_update(org_id, col_id, headers, data, conn, ip).await + post_organization_collection_update(org_id, col_id, headers, data, conn).await } #[post("/organizations/<org_id>/collections/<col_id>", data = "<data>")] @@ -445,7 +441,6 @@ async fn post_organization_collection_update( headers: ManagerHeaders, data: JsonUpcase<NewCollectionData>, mut conn: DbConn, - ip: ClientIp, ) -> JsonResult { let data: NewCollectionData = data.into_inner().data; @@ -472,7 +467,7 @@ async fn post_organization_collection_update( org_id, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -548,7 +543,6 @@ async fn delete_organization_collection( col_id: String, headers: ManagerHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { match Collection::find_by_uuid(&col_id, &mut conn).await { None => err!("Collection not found"), @@ -560,7 +554,7 @@ async fn delete_organization_collection( org_id, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -586,9 +580,8 @@ async fn post_organization_collection_delete( headers: ManagerHeaders, _data: JsonUpcase<DeleteCollectionData>, conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - delete_organization_collection(org_id, col_id, headers, conn, ip).await + delete_organization_collection(org_id, col_id, headers, conn).await } #[get("/organizations/<org_id>/collections/<coll_id>/details")] @@ -828,7 +821,6 @@ async fn send_invite( data: JsonUpcase<InviteData>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { let data: InviteData = data.into_inner().data; @@ -903,7 +895,7 @@ async fn send_invite( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1101,7 +1093,6 @@ async fn bulk_confirm_invite( data: JsonUpcase<Value>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> Json<Value> { let data = data.into_inner().data; @@ -1112,8 +1103,7 @@ async fn bulk_confirm_invite( for invite in keys { let org_user_id = invite["Id"].as_str().unwrap_or_default(); let user_key = invite["Key"].as_str().unwrap_or_default(); - let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &ip, &nt).await - { + let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &nt).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1144,12 +1134,11 @@ async fn confirm_invite( data: JsonUpcase<Value>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data = data.into_inner().data; let user_key = data["Key"].as_str().unwrap_or_default(); - _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &ip, &nt).await + _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &nt).await } async fn _confirm_invite( @@ -1158,7 +1147,6 @@ async fn _confirm_invite( key: &str, headers: &AdminHeaders, conn: &mut DbConn, - ip: &ClientIp, nt: &Notify<'_>, ) -> EmptyResult { if key.is_empty() || org_user_id.is_empty() { @@ -1201,7 +1189,7 @@ async fn _confirm_invite( String::from(org_id), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, conn, ) .await; @@ -1264,9 +1252,8 @@ async fn put_organization_user( data: JsonUpcase<EditUserData>, headers: AdminHeaders, conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - edit_user(org_id, org_user_id, data, headers, conn, ip).await + edit_user(org_id, org_user_id, data, headers, conn).await } #[post("/organizations/<org_id>/users/<org_user_id>", data = "<data>", rank = 1)] @@ -1276,7 +1263,6 @@ async fn edit_user( data: JsonUpcase<EditUserData>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { let data: EditUserData = data.into_inner().data; @@ -1365,7 +1351,7 @@ async fn edit_user( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1379,14 +1365,13 @@ async fn bulk_delete_user( data: JsonUpcase<OrgBulkIds>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> Json<Value> { let data: OrgBulkIds = data.into_inner().data; let mut bulk_response = Vec::new(); for org_user_id in data.Ids { - let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await { + let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1413,10 +1398,9 @@ async fn delete_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await + _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await } #[post("/organizations/<org_id>/users/<org_user_id>/delete")] @@ -1425,10 +1409,9 @@ async fn post_delete_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { - _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await + _delete_user(&org_id, &org_user_id, &headers, &mut conn, &nt).await } async fn _delete_user( @@ -1436,7 +1419,6 @@ async fn _delete_user( org_user_id: &str, headers: &AdminHeaders, conn: &mut DbConn, - ip: &ClientIp, nt: &Notify<'_>, ) -> EmptyResult { let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { @@ -1461,7 +1443,7 @@ async fn _delete_user( String::from(org_id), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, conn, ) .await; @@ -1536,7 +1518,6 @@ async fn post_org_import( data: JsonUpcase<ImportData>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let data: ImportData = data.into_inner().data; @@ -1569,9 +1550,7 @@ async fn post_org_import( let mut ciphers = Vec::new(); for cipher_data in data.Ciphers { let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); - update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None) - .await - .ok(); + update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &mut conn, &nt, UpdateType::None).await.ok(); ciphers.push(cipher); } @@ -1657,7 +1636,6 @@ async fn put_policy( data: Json<PolicyData>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> JsonResult { let data: PolicyData = data.into_inner(); @@ -1690,7 +1668,7 @@ async fn put_policy( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1724,7 +1702,7 @@ async fn put_policy( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1749,7 +1727,7 @@ async fn put_policy( org_id, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1825,13 +1803,7 @@ struct OrgImportData { } #[post("/organizations/<org_id>/import", data = "<data>")] -async fn import( - org_id: String, - data: JsonUpcase<OrgImportData>, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, -) -> EmptyResult { +async fn import(org_id: String, data: JsonUpcase<OrgImportData>, 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 @@ -1857,7 +1829,7 @@ async fn import( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1887,7 +1859,7 @@ async fn import( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1923,7 +1895,7 @@ async fn import( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -1944,9 +1916,8 @@ async fn deactivate_organization_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await + _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn).await } // Pre web-vault v2022.9.x endpoint @@ -1956,9 +1927,8 @@ async fn bulk_deactivate_organization_user( data: JsonUpcase<Value>, headers: AdminHeaders, conn: DbConn, - ip: ClientIp, ) -> Json<Value> { - bulk_revoke_organization_user(org_id, data, headers, conn, ip).await + bulk_revoke_organization_user(org_id, data, headers, conn).await } #[put("/organizations/<org_id>/users/<org_user_id>/revoke")] @@ -1967,9 +1937,8 @@ async fn revoke_organization_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await + _revoke_organization_user(&org_id, &org_user_id, &headers, &mut conn).await } #[put("/organizations/<org_id>/users/revoke", data = "<data>")] @@ -1978,7 +1947,6 @@ async fn bulk_revoke_organization_user( data: JsonUpcase<Value>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> Json<Value> { let data = data.into_inner().data; @@ -1987,7 +1955,7 @@ async fn bulk_revoke_organization_user( Some(org_users) => { for org_user_id in org_users { let org_user_id = org_user_id.as_str().unwrap_or_default(); - let err_msg = match _revoke_organization_user(&org_id, org_user_id, &headers, &mut conn, &ip).await { + let err_msg = match _revoke_organization_user(&org_id, org_user_id, &headers, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2016,7 +1984,6 @@ async fn _revoke_organization_user( org_user_id: &str, headers: &AdminHeaders, conn: &mut DbConn, - ip: &ClientIp, ) -> 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 => { @@ -2041,7 +2008,7 @@ async fn _revoke_organization_user( org_id.to_string(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, conn, ) .await; @@ -2059,9 +2026,8 @@ async fn activate_organization_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await + _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn).await } // Pre web-vault v2022.9.x endpoint @@ -2071,9 +2037,8 @@ async fn bulk_activate_organization_user( data: JsonUpcase<Value>, headers: AdminHeaders, conn: DbConn, - ip: ClientIp, ) -> Json<Value> { - bulk_restore_organization_user(org_id, data, headers, conn, ip).await + bulk_restore_organization_user(org_id, data, headers, conn).await } #[put("/organizations/<org_id>/users/<org_user_id>/restore")] @@ -2082,9 +2047,8 @@ async fn restore_organization_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await + _restore_organization_user(&org_id, &org_user_id, &headers, &mut conn).await } #[put("/organizations/<org_id>/users/restore", data = "<data>")] @@ -2093,7 +2057,6 @@ async fn bulk_restore_organization_user( data: JsonUpcase<Value>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> Json<Value> { let data = data.into_inner().data; @@ -2102,7 +2065,7 @@ async fn bulk_restore_organization_user( Some(org_users) => { for org_user_id in org_users { let org_user_id = org_user_id.as_str().unwrap_or_default(); - let err_msg = match _restore_organization_user(&org_id, org_user_id, &headers, &mut conn, &ip).await { + let err_msg = match _restore_organization_user(&org_id, org_user_id, &headers, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2131,7 +2094,6 @@ async fn _restore_organization_user( org_user_id: &str, headers: &AdminHeaders, conn: &mut DbConn, - ip: &ClientIp, ) -> 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 => { @@ -2165,7 +2127,7 @@ async fn _restore_organization_user( org_id.to_string(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, conn, ) .await; @@ -2277,9 +2239,8 @@ async fn post_group( data: JsonUpcase<GroupRequest>, headers: AdminHeaders, conn: DbConn, - ip: ClientIp, ) -> JsonResult { - put_group(org_id, group_id, data, headers, conn, ip).await + put_group(org_id, group_id, data, headers, conn).await } #[post("/organizations/<org_id>/groups", data = "<data>")] @@ -2288,7 +2249,6 @@ async fn post_groups( headers: AdminHeaders, data: JsonUpcase<GroupRequest>, mut conn: DbConn, - ip: ClientIp, ) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2303,12 +2263,12 @@ async fn post_groups( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; - add_update_group(group, group_request.Collections, group_request.Users, &org_id, &headers, &ip, &mut conn).await + add_update_group(group, group_request.Collections, group_request.Users, &org_id, &headers, &mut conn).await } #[put("/organizations/<org_id>/groups/<group_id>", data = "<data>")] @@ -2318,7 +2278,6 @@ async fn put_group( data: JsonUpcase<GroupRequest>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2341,13 +2300,12 @@ async fn put_group( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; - add_update_group(updated_group, group_request.Collections, group_request.Users, &org_id, &headers, &ip, &mut conn) - .await + add_update_group(updated_group, group_request.Collections, group_request.Users, &org_id, &headers, &mut conn).await } async fn add_update_group( @@ -2356,7 +2314,6 @@ async fn add_update_group( users: Vec<String>, org_id: &str, headers: &AdminHeaders, - ip: &ClientIp, conn: &mut DbConn, ) -> JsonResult { group.save(conn).await?; @@ -2376,7 +2333,7 @@ async fn add_update_group( String::from(org_id), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, conn, ) .await; @@ -2406,24 +2363,12 @@ async fn get_group_details(_org_id: String, group_id: String, _headers: AdminHea } #[post("/organizations/<org_id>/groups/<group_id>/delete")] -async fn post_delete_group( - org_id: String, - group_id: String, - headers: AdminHeaders, - conn: DbConn, - ip: ClientIp, -) -> EmptyResult { - delete_group(org_id, group_id, headers, conn, ip).await +async fn post_delete_group(org_id: String, group_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult { + delete_group(org_id, group_id, headers, conn).await } #[delete("/organizations/<org_id>/groups/<group_id>")] -async fn delete_group( - org_id: String, - group_id: String, - headers: AdminHeaders, - mut conn: DbConn, - ip: ClientIp, -) -> EmptyResult { +async fn delete_group(org_id: String, group_id: String, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } @@ -2439,7 +2384,7 @@ async fn delete_group( org_id, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -2488,7 +2433,6 @@ async fn put_group_users( headers: AdminHeaders, data: JsonVec<String>, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2512,7 +2456,7 @@ async fn put_group_users( org_id.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -2551,9 +2495,8 @@ async fn post_user_groups( data: JsonUpcase<OrganizationUserUpdateGroupsRequest>, headers: AdminHeaders, conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - put_user_groups(org_id, org_user_id, data, headers, conn, ip).await + put_user_groups(org_id, org_user_id, data, headers, conn).await } #[put("/organizations/<org_id>/users/<org_user_id>/groups", data = "<data>")] @@ -2563,7 +2506,6 @@ async fn put_user_groups( data: JsonUpcase<OrganizationUserUpdateGroupsRequest>, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2588,7 +2530,7 @@ async fn put_user_groups( org_id, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -2603,9 +2545,8 @@ async fn post_delete_group_user( org_user_id: String, headers: AdminHeaders, conn: DbConn, - ip: ClientIp, ) -> EmptyResult { - delete_group_user(org_id, group_id, org_user_id, headers, conn, ip).await + delete_group_user(org_id, group_id, org_user_id, headers, conn).await } #[delete("/organizations/<org_id>/groups/<group_id>/users/<org_user_id>")] @@ -2615,7 +2556,6 @@ async fn delete_group_user( org_user_id: String, headers: AdminHeaders, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); @@ -2637,7 +2577,7 @@ async fn delete_group_user( org_id, headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -2679,7 +2619,6 @@ async fn put_reset_password( headers: AdminHeaders, data: JsonUpcase<OrganizationUserResetPasswordRequest>, mut conn: DbConn, - ip: ClientIp, nt: Notify<'_>, ) -> EmptyResult { let org = match Organization::find_by_uuid(&org_id, &mut conn).await { @@ -2725,7 +2664,7 @@ async fn put_reset_password( org.uuid.clone(), headers.user.uuid.clone(), headers.device.atype, - &ip.ip, + &headers.ip.ip, &mut conn, ) .await; @@ -2812,7 +2751,6 @@ async fn put_reset_password_enrollment( headers: Headers, data: JsonUpcase<OrganizationUserResetPasswordEnrollmentRequest>, mut conn: DbConn, - ip: ClientIp, ) -> EmptyResult { let mut org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &mut conn).await { Some(u) => u, @@ -2838,7 +2776,8 @@ async fn put_reset_password_enrollment( EventType::OrganizationUserResetPasswordWithdraw as i32 }; - log_event(log_id, &org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &ip.ip, &mut conn).await; + log_event(log_id, &org_user_id, org_id, headers.user.uuid.clone(), headers.device.atype, &headers.ip.ip, &mut conn) + .await; Ok(()) } diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs @@ -57,7 +57,6 @@ struct EnableAuthenticatorData { async fn activate_authenticator( data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, - ip: ClientIp, mut conn: DbConn, ) -> JsonResult { let data: EnableAuthenticatorData = data.into_inner().data; @@ -82,11 +81,11 @@ async fn activate_authenticator( } // Validate the token provided with the key, and save new twofactor - validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &ip, &mut conn).await?; + validate_totp_code(&user.uuid, &token, &key.to_uppercase(), &headers.ip, &mut conn).await?; _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; Ok(Json(json!({ "Enabled": true, @@ -99,10 +98,9 @@ async fn activate_authenticator( async fn activate_authenticator_put( data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, - ip: ClientIp, conn: DbConn, ) -> JsonResult { - activate_authenticator(data, headers, ip, conn).await + activate_authenticator(data, headers, conn).await } pub async fn validate_totp_code_str( diff --git a/src/api/core/two_factor/duo.rs b/src/api/core/two_factor/duo.rs @@ -8,7 +8,7 @@ use crate::{ core::log_user_event, core::two_factor::_generate_recover_code, ApiResult, EmptyResult, JsonResult, JsonUpcase, PasswordData, }, - auth::{ClientIp, Headers}, + auth::Headers, crypto, db::{ models::{EventType, TwoFactor, TwoFactorType, User}, @@ -155,7 +155,7 @@ fn check_duo_fields_custom(data: &EnableDuoData) -> bool { } #[post("/two-factor/duo", data = "<data>")] -async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut conn: DbConn, ip: ClientIp) -> JsonResult { +async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EnableDuoData = data.into_inner().data; let mut user = headers.user; @@ -178,7 +178,7 @@ async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut con _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; Ok(Json(json!({ "Enabled": true, @@ -190,8 +190,8 @@ async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut con } #[put("/two-factor/duo", data = "<data>")] -async fn activate_duo_put(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbConn, ip: ClientIp) -> JsonResult { - activate_duo(data, headers, conn, ip).await +async fn activate_duo_put(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbConn) -> JsonResult { + activate_duo(data, headers, conn).await } async fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> EmptyResult { diff --git a/src/api/core/two_factor/email.rs b/src/api/core/two_factor/email.rs @@ -7,7 +7,7 @@ use crate::{ core::{log_user_event, two_factor::_generate_recover_code}, EmptyResult, JsonResult, JsonUpcase, PasswordData, }, - auth::{ClientIp, Headers}, + auth::Headers, crypto, db::{ models::{EventType, TwoFactor, TwoFactorType}, @@ -90,7 +90,7 @@ async fn get_email(data: JsonUpcase<PasswordData>, headers: Headers, mut conn: D let twofactor_data = EmailTokenData::from_json(&x.data)?; (true, json!(twofactor_data.email)) } - _ => (false, json!(null)), + _ => (false, serde_json::value::Value::Null), }; Ok(Json(json!({ @@ -150,7 +150,7 @@ struct EmailData { /// Verify email belongs to user and can be used for 2FA email codes. #[put("/two-factor/email", data = "<data>")] -async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn, ip: ClientIp) -> JsonResult { +async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EmailData = data.into_inner().data; let mut user = headers.user; @@ -180,7 +180,7 @@ async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn, _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; Ok(Json(json!({ "Email": email_data.email, diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs @@ -6,7 +6,7 @@ use serde_json::Value; use crate::{ api::{core::log_user_event, JsonResult, JsonUpcase, NumberOrString, PasswordData}, - auth::{ClientHeaders, ClientIp, Headers}, + auth::{ClientHeaders, Headers}, crypto, db::{models::*, DbConn, DbPool}, mail, CONFIG, @@ -73,12 +73,7 @@ struct RecoverTwoFactor { } #[post("/two-factor/recover", data = "<data>")] -async fn recover( - data: JsonUpcase<RecoverTwoFactor>, - client_headers: ClientHeaders, - mut conn: DbConn, - ip: ClientIp, -) -> JsonResult { +async fn recover(data: JsonUpcase<RecoverTwoFactor>, client_headers: ClientHeaders, mut conn: DbConn) -> JsonResult { let data: RecoverTwoFactor = data.into_inner().data; use crate::db::models::User; @@ -102,12 +97,19 @@ async fn recover( // Remove all twofactors from the user TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?; - log_user_event(EventType::UserRecovered2fa as i32, &user.uuid, client_headers.device_type, &ip.ip, &mut conn).await; + log_user_event( + EventType::UserRecovered2fa as i32, + &user.uuid, + client_headers.device_type, + &client_headers.ip.ip, + &mut conn, + ) + .await; // Remove the recovery code, not needed without twofactors user.totp_recover = None; user.save(&mut conn).await?; - Ok(Json(json!({}))) + Ok(Json(Value::Object(serde_json::Map::new()))) } async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) { @@ -126,12 +128,7 @@ struct DisableTwoFactorData { } #[post("/two-factor/disable", data = "<data>")] -async fn disable_twofactor( - data: JsonUpcase<DisableTwoFactorData>, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, -) -> JsonResult { +async fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, mut conn: DbConn) -> JsonResult { let data: DisableTwoFactorData = data.into_inner().data; let password_hash = data.MasterPasswordHash; let user = headers.user; @@ -144,7 +141,8 @@ async fn disable_twofactor( if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await { twofactor.delete(&mut conn).await?; - log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; + log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn) + .await; } let twofactor_disabled = TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty(); @@ -173,13 +171,8 @@ async fn disable_twofactor( } #[put("/two-factor/disable", data = "<data>")] -async fn disable_twofactor_put( - data: JsonUpcase<DisableTwoFactorData>, - headers: Headers, - conn: DbConn, - ip: ClientIp, -) -> JsonResult { - disable_twofactor(data, headers, conn, ip).await +async fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult { + disable_twofactor(data, headers, conn).await } pub async fn send_incomplete_2fa_notifications(pool: DbPool) { diff --git a/src/api/core/two_factor/webauthn.rs b/src/api/core/two_factor/webauthn.rs @@ -9,7 +9,7 @@ use crate::{ core::{log_user_event, two_factor::_generate_recover_code}, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData, }, - auth::{ClientIp, Headers}, + auth::Headers, db::{ models::{EventType, TwoFactor, TwoFactorType}, DbConn, @@ -242,12 +242,7 @@ impl From<PublicKeyCredentialCopy> for PublicKeyCredential { } #[post("/two-factor/webauthn", data = "<data>")] -async fn activate_webauthn( - data: JsonUpcase<EnableWebauthnData>, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, -) -> JsonResult { +async fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EnableWebauthnData = data.into_inner().data; let mut user = headers.user; @@ -286,7 +281,7 @@ async fn activate_webauthn( .await?; _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; let keys_json: Vec<Value> = registrations.iter().map(WebauthnRegistration::to_json).collect(); Ok(Json(json!({ @@ -297,13 +292,8 @@ async fn activate_webauthn( } #[put("/two-factor/webauthn", data = "<data>")] -async fn activate_webauthn_put( - data: JsonUpcase<EnableWebauthnData>, - headers: Headers, - conn: DbConn, - ip: ClientIp, -) -> JsonResult { - activate_webauthn(data, headers, conn, ip).await +async fn activate_webauthn_put(data: JsonUpcase<EnableWebauthnData>, headers: Headers, conn: DbConn) -> JsonResult { + activate_webauthn(data, headers, conn).await } #[derive(Deserialize, Debug)] diff --git a/src/api/core/two_factor/yubikey.rs b/src/api/core/two_factor/yubikey.rs @@ -8,7 +8,7 @@ use crate::{ core::{log_user_event, two_factor::_generate_recover_code}, EmptyResult, JsonResult, JsonUpcase, PasswordData, }, - auth::{ClientIp, Headers}, + auth::Headers, db::{ models::{EventType, TwoFactor, TwoFactorType}, DbConn, @@ -47,7 +47,7 @@ fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> { } fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value { - let mut result = json!({}); + let mut result = Value::Object(serde_json::Map::new()); for (i, key) in yubikeys.into_iter().enumerate() { result[format!("Key{}", i + 1)] = Value::String(key); @@ -118,12 +118,7 @@ async fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, mut } #[post("/two-factor/yubikey", data = "<data>")] -async fn activate_yubikey( - data: JsonUpcase<EnableYubikeyData>, - headers: Headers, - mut conn: DbConn, - ip: ClientIp, -) -> JsonResult { +async fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, mut conn: DbConn) -> JsonResult { let data: EnableYubikeyData = data.into_inner().data; let mut user = headers.user; @@ -169,7 +164,7 @@ async fn activate_yubikey( _generate_recover_code(&mut user, &mut conn).await; - log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &ip.ip, &mut conn).await; + log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; let mut result = jsonify_yubikeys(yubikey_metadata.Keys); @@ -181,13 +176,8 @@ async fn activate_yubikey( } #[put("/two-factor/yubikey", data = "<data>")] -async fn activate_yubikey_put( - data: JsonUpcase<EnableYubikeyData>, - headers: Headers, - conn: DbConn, - ip: ClientIp, -) -> JsonResult { - activate_yubikey(data, headers, conn, ip).await +async fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult { + activate_yubikey(data, headers, conn).await } pub async fn validate_yubikey_login(response: &str, twofactor_data: &str) -> EmptyResult { diff --git a/src/api/identity.rs b/src/api/identity.rs @@ -25,7 +25,7 @@ pub fn routes() -> Vec<Route> { } #[post("/connect/token", data = "<data>")] -async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: DbConn, ip: ClientIp) -> JsonResult { +async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: DbConn) -> JsonResult { let data: ConnectData = data.into_inner(); let mut user_uuid: Option<String> = None; @@ -45,7 +45,7 @@ async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: _check_is_some(&data.device_name, "device_name cannot be blank")?; _check_is_some(&data.device_type, "device_type cannot be blank")?; - _password_login(data, &mut user_uuid, &mut conn, &ip).await + _password_login(data, &mut user_uuid, &mut conn, &client_header.ip).await } "client_credentials" => { _check_is_some(&data.client_id, "client_id cannot be blank")?; @@ -56,7 +56,7 @@ async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: _check_is_some(&data.device_name, "device_name cannot be blank")?; _check_is_some(&data.device_type, "device_type cannot be blank")?; - _api_key_login(data, &mut user_uuid, &mut conn, &ip).await + _api_key_login(data, &mut user_uuid, &mut conn, &client_header.ip).await } t => err!("Invalid type", t), }; @@ -68,14 +68,21 @@ async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: EventType::UserLoggedIn as i32, &user_uuid, client_header.device_type, - &ip.ip, + &client_header.ip.ip, &mut conn, ) .await; } Err(e) => { if let Some(ev) = e.get_event() { - log_user_event(ev.event as i32, &user_uuid, client_header.device_type, &ip.ip, &mut conn).await + log_user_event( + ev.event as i32, + &user_uuid, + client_header.device_type, + &client_header.ip.ip, + &mut conn, + ) + .await } } } diff --git a/src/api/notifications.rs b/src/api/notifications.rs @@ -10,8 +10,7 @@ use std::{ use chrono::NaiveDateTime; use futures::{SinkExt, StreamExt}; use rmpv::Value; -use rocket::{serde::json::Json, Route}; -use serde_json::Value as JsonValue; +use rocket::Route; use tokio::{ net::{TcpListener, TcpStream}, sync::mpsc::Sender, @@ -23,13 +22,12 @@ use tokio_tungstenite::{ use crate::{ api::EmptyResult, - auth::Headers, db::models::{Cipher, Folder, Send, User}, Error, CONFIG, }; pub fn routes() -> Vec<Route> { - routes![negotiate, websockets_err] + routes![websockets_err] } #[get("/hub")] @@ -51,29 +49,6 @@ fn websockets_err() -> EmptyResult { } } -#[post("/hub/negotiate")] -fn negotiate(_headers: Headers) -> Json<JsonValue> { - use crate::crypto; - use data_encoding::BASE64URL; - - let conn_id = crypto::encode_random_bytes::<16>(BASE64URL); - let mut available_transports: Vec<JsonValue> = Vec::new(); - - if CONFIG.websocket_enabled() { - available_transports.push(json!({"transport":"WebSockets", "transferFormats":["Text","Binary"]})); - } - - // TODO: Implement transports - // Rocket WS support: https://github.com/SergioBenitez/Rocket/issues/90 - // Rocket SSE support: https://github.com/SergioBenitez/Rocket/issues/33 - // {"transport":"ServerSentEvents", "transferFormats":["Text"]}, - // {"transport":"LongPolling", "transferFormats":["Text","Binary"]} - Json(json!({ - "connectionId": conn_id, - "availableTransports": available_transports - })) -} - // // Websockets server // diff --git a/src/auth.rs b/src/auth.rs @@ -318,6 +318,7 @@ impl<'r> FromRequest<'r> for Host { pub struct ClientHeaders { pub host: String, pub device_type: i32, + pub ip: ClientIp, } #[rocket::async_trait] @@ -326,6 +327,10 @@ impl<'r> FromRequest<'r> for ClientHeaders { async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { let host = try_outcome!(Host::from_request(request).await).host; + let ip = match ClientIp::from_request(request).await { + Outcome::Success(ip) => ip, + _ => err_handler!("Error getting Client IP"), + }; // When unknown or unable to parse, return 14, which is 'Unknown Browser' let device_type: i32 = request.headers().get_one("device-type").map(|d| d.parse().unwrap_or(14)).unwrap_or_else(|| 14); @@ -333,6 +338,7 @@ impl<'r> FromRequest<'r> for ClientHeaders { Outcome::Success(ClientHeaders { host, device_type, + ip, }) } } @@ -341,6 +347,7 @@ pub struct Headers { pub host: String, pub device: Device, pub user: User, + pub ip: ClientIp, } #[rocket::async_trait] @@ -351,6 +358,10 @@ impl<'r> FromRequest<'r> for Headers { let headers = request.headers(); let host = try_outcome!(Host::from_request(request).await).host; + let ip = match ClientIp::from_request(request).await { + Outcome::Success(ip) => ip, + _ => err_handler!("Error getting Client IP"), + }; // Get access_token let access_token: &str = match headers.get_one("Authorization") { @@ -420,6 +431,7 @@ impl<'r> FromRequest<'r> for Headers { host, device, user, + ip, }) } } @@ -431,6 +443,7 @@ pub struct OrgHeaders { pub org_user_type: UserOrgType, pub org_user: UserOrganization, pub org_id: String, + pub ip: ClientIp, } // org_id is usually the second path param ("/organizations/<org_id>"), @@ -491,6 +504,7 @@ impl<'r> FromRequest<'r> for OrgHeaders { }, org_user, org_id, + ip: headers.ip, }) } _ => err_handler!("Error getting the organization id"), @@ -504,6 +518,7 @@ pub struct AdminHeaders { pub user: User, pub org_user_type: UserOrgType, pub client_version: Option<String>, + pub ip: ClientIp, } #[rocket::async_trait] @@ -520,6 +535,7 @@ impl<'r> FromRequest<'r> for AdminHeaders { user: headers.user, org_user_type: headers.org_user_type, client_version, + ip: headers.ip, }) } else { err_handler!("You need to be Admin or Owner to call this endpoint") @@ -533,6 +549,7 @@ impl From<AdminHeaders> for Headers { host: h.host, device: h.device, user: h.user, + ip: h.ip, } } } @@ -564,6 +581,7 @@ pub struct ManagerHeaders { pub device: Device, pub user: User, pub org_user_type: UserOrgType, + pub ip: ClientIp, } #[rocket::async_trait] @@ -599,6 +617,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders { device: headers.device, user: headers.user, org_user_type: headers.org_user_type, + ip: headers.ip, }) } else { err_handler!("You need to be a Manager, Admin or Owner to call this endpoint") @@ -612,6 +631,7 @@ impl From<ManagerHeaders> for Headers { host: h.host, device: h.device, user: h.user, + ip: h.ip, } } } @@ -623,6 +643,7 @@ pub struct ManagerHeadersLoose { pub device: Device, pub user: User, pub org_user_type: UserOrgType, + pub ip: ClientIp, } #[rocket::async_trait] @@ -637,6 +658,7 @@ impl<'r> FromRequest<'r> for ManagerHeadersLoose { device: headers.device, user: headers.user, org_user_type: headers.org_user_type, + ip: headers.ip, }) } else { err_handler!("You need to be a Manager, Admin or Owner to call this endpoint") @@ -650,6 +672,7 @@ impl From<ManagerHeadersLoose> for Headers { host: h.host, device: h.device, user: h.user, + ip: h.ip, } } } @@ -658,6 +681,7 @@ pub struct OwnerHeaders { pub host: String, pub device: Device, pub user: User, + pub ip: ClientIp, } #[rocket::async_trait] @@ -671,6 +695,7 @@ impl<'r> FromRequest<'r> for OwnerHeaders { host: headers.host, device: headers.device, user: headers.user, + ip: headers.ip, }) } else { err_handler!("You need to be Owner to call this endpoint") diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs @@ -151,7 +151,8 @@ impl Cipher { // Get the type_data or a default to an empty json object '{}'. // If not passing an empty object, mobile clients will crash. - let mut type_data_json: Value = serde_json::from_str(&self.data).unwrap_or_else(|_| json!({})); + let mut type_data_json: Value = + serde_json::from_str(&self.data).unwrap_or_else(|_| Value::Object(serde_json::Map::new())); // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream // Set the first element of the Uris array as Uri, this is needed several (mobile) clients. @@ -170,10 +171,10 @@ impl Cipher { // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream // data_json should always contain the following keys with every atype - data_json["Fields"] = json!(fields_json); + data_json["Fields"] = fields_json.clone(); data_json["Name"] = json!(self.name); data_json["Notes"] = json!(self.notes); - data_json["PasswordHistory"] = json!(password_history_json); + data_json["PasswordHistory"] = password_history_json.clone(); let collection_ids = if let Some(cipher_sync_data) = cipher_sync_data { if let Some(cipher_collections) = cipher_sync_data.cipher_collections.get(&self.uuid) { diff --git a/src/main.rs b/src/main.rs @@ -250,6 +250,14 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { log::LevelFilter::Off }; + // Only show rocket underscore `_` logs when the level is Debug or higher + // Else this will bloat the log output with useless messages. + let rocket_underscore_level = if level >= log::LevelFilter::Debug { + log::LevelFilter::Warn + } else { + log::LevelFilter::Off + }; + let mut logger = fern::Dispatch::new() .level(level) // Hide unknown certificate errors if using self-signed @@ -257,7 +265,7 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { // Hide failed to close stream messages .level_for("hyper::server", log::LevelFilter::Warn) // Silence rocket logs - .level_for("_", log::LevelFilter::Warn) + .level_for("_", rocket_underscore_level) .level_for("rocket::launch", log::LevelFilter::Error) .level_for("rocket::launch_", log::LevelFilter::Error) .level_for("rocket::rocket", log::LevelFilter::Warn) @@ -269,7 +277,8 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { // Prevent cookie_store logs .level_for("cookie_store", log::LevelFilter::Off) // Variable level for trust-dns used by reqwest - .level_for("trust_dns_proto", trust_dns_level) + .level_for("trust_dns_resolver::name_server::name_server", trust_dns_level) + .level_for("trust_dns_proto::xfer", trust_dns_level) .level_for("diesel_logger", diesel_logger_level) .chain(std::io::stdout()); @@ -277,9 +286,9 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> { // This can contain sensitive information we do not want in the default debug/trace logging. if CONFIG.smtp_debug() { println!( - "[WARNING] SMTP Debugging is enabled (SMTP_DEBUG=true). Sensitive information could be disclosed via logs!" + "[WARNING] SMTP Debugging is enabled (SMTP_DEBUG=true). Sensitive information could be disclosed via logs!\n\ + [WARNING] Only enable SMTP_DEBUG during troubleshooting!\n" ); - println!("[WARNING] Only enable SMTP_DEBUG during troubleshooting!\n"); logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Debug) } else { logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Off) diff --git a/src/util.rs b/src/util.rs @@ -231,8 +231,7 @@ impl<'r> FromParam<'r> for SafeString { // Log all the routes from the main paths list, and the attachments endpoint // Effectively ignores, any static file route, and the alive endpoint -const LOGGED_ROUTES: [&str; 6] = - ["/api", "/admin", "/identity", "/icons", "/notifications/hub/negotiate", "/attachments"]; +const LOGGED_ROUTES: [&str; 5] = ["/api", "/admin", "/identity", "/icons", "/attachments"]; // Boolean is extra debug, when true, we ignore the whitelist above and also print the mounts pub struct BetterLogging(pub bool); @@ -588,7 +587,7 @@ impl<'de> Visitor<'de> for UpCaseVisitor { fn upcase_value(value: Value) -> Value { if let Value::Object(map) = value { - let mut new_value = json!({}); + let mut new_value = Value::Object(serde_json::Map::new()); for (key, val) in map.into_iter() { let processed_key = _process_key(&key); @@ -597,7 +596,7 @@ fn upcase_value(value: Value) -> Value { new_value } else if let Value::Array(array) = value { // Initialize array with null values - let mut new_value = json!(vec![Value::Null; array.len()]); + let mut new_value = Value::Array(vec![Value::Null; array.len()]); for (index, val) in array.into_iter().enumerate() { new_value[index] = upcase_value(val);