Fix mobile push blocking requests and spamming push server

This commit is contained in:
Bernd Schoolmann 2023-06-16 23:34:16 +02:00
parent 5b7d7390b0
commit e4606431d1
No known key found for this signature in database
7 changed files with 148 additions and 102 deletions

View File

@ -403,10 +403,10 @@ async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyRe
async fn deauth_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { async fn deauth_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(uuid, &mut conn).await?; let mut user = get_user_or_404(uuid, &mut conn).await?;
nt.send_logout(&user, None, &mut conn).await; nt.send_logout(&user, None).await;
if CONFIG.push_enabled() { if CONFIG.push_enabled() {
for device in Device::find_push_device_by_user(&user.uuid, &mut conn).await { for device in Device::find_push_devices_by_user(&user.uuid, &mut conn).await {
match unregister_push_device(device.uuid).await { match unregister_push_device(device.uuid).await {
Ok(r) => r, Ok(r) => r,
Err(e) => error!("Unable to unregister devices from Bitwarden server: {}", e), Err(e) => error!("Unable to unregister devices from Bitwarden server: {}", e),
@ -429,7 +429,7 @@ async fn disable_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Noti
let save_result = user.save(&mut conn).await; let save_result = user.save(&mut conn).await;
nt.send_logout(&user, None, &mut conn).await; nt.send_logout(&user, None).await;
save_result save_result
} }

View File

@ -343,7 +343,7 @@ async fn post_password(
// Prevent loging out the client where the user requested this endpoint from. // Prevent loging out the client where the user requested this endpoint from.
// If you do logout the user it will causes issues at the client side. // If you do logout the user it will causes issues at the client side.
// Adding the device uuid will prevent this. // Adding the device uuid will prevent this.
nt.send_logout(&user, Some(headers.device.uuid), &mut conn).await; nt.send_logout(&user, Some(headers.device.uuid)).await;
save_result save_result
} }
@ -403,7 +403,7 @@ async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: D
user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None); user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None);
let save_result = user.save(&mut conn).await; let save_result = user.save(&mut conn).await;
nt.send_logout(&user, Some(headers.device.uuid), &mut conn).await; nt.send_logout(&user, Some(headers.device.uuid)).await;
save_result save_result
} }
@ -490,7 +490,7 @@ async fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, mut conn: D
// Prevent loging out the client where the user requested this endpoint from. // Prevent loging out the client where the user requested this endpoint from.
// If you do logout the user it will causes issues at the client side. // If you do logout the user it will causes issues at the client side.
// Adding the device uuid will prevent this. // Adding the device uuid will prevent this.
nt.send_logout(&user, Some(headers.device.uuid), &mut conn).await; nt.send_logout(&user, Some(headers.device.uuid)).await;
save_result save_result
} }
@ -513,7 +513,7 @@ async fn post_sstamp(
user.reset_security_stamp(); user.reset_security_stamp();
let save_result = user.save(&mut conn).await; let save_result = user.save(&mut conn).await;
nt.send_logout(&user, None, &mut conn).await; nt.send_logout(&user, None).await;
save_result save_result
} }
@ -616,7 +616,7 @@ async fn post_email(
let save_result = user.save(&mut conn).await; let save_result = user.save(&mut conn).await;
nt.send_logout(&user, None, &mut conn).await; nt.send_logout(&user, None).await;
save_result save_result
} }

View File

@ -2718,7 +2718,7 @@ async fn put_reset_password(
user.set_password(reset_request.NewMasterPasswordHash.as_str(), Some(reset_request.Key), true, None); user.set_password(reset_request.NewMasterPasswordHash.as_str(), Some(reset_request.Key), true, None);
user.save(&mut conn).await?; user.save(&mut conn).await?;
nt.send_logout(&user, None, &mut conn).await; nt.send_logout(&user, None).await;
log_event( log_event(
EventType::OrganizationUserAdminResetPassword as i32, EventType::OrganizationUserAdminResetPassword as i32,

View File

@ -180,8 +180,14 @@ async fn post_send(data: JsonUpcase<SendData>, headers: Headers, mut conn: DbCon
let mut send = create_send(data, headers.user.uuid)?; let mut send = create_send(data, headers.user.uuid)?;
send.save(&mut conn).await?; send.save(&mut conn).await?;
nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&mut conn).await, &mut conn) nt.send_send_update(
.await; UpdateType::SyncSendCreate,
&send,
&send.update_users_revision(&mut conn).await,
&headers.device.uuid,
&mut conn,
)
.await;
Ok(Json(send.to_json())) Ok(Json(send.to_json()))
} }
@ -253,8 +259,14 @@ async fn post_send_file(data: Form<UploadData<'_>>, headers: Headers, mut conn:
// Save the changes in the database // Save the changes in the database
send.save(&mut conn).await?; send.save(&mut conn).await?;
nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&mut conn).await, &mut conn) nt.send_send_update(
.await; UpdateType::SyncSendCreate,
&send,
&send.update_users_revision(&mut conn).await,
&headers.device.uuid,
&mut conn,
)
.await;
Ok(Json(send.to_json())) Ok(Json(send.to_json()))
} }
@ -337,8 +349,14 @@ async fn post_send_file_v2_data(
data.data.move_copy_to(file_path).await? data.data.move_copy_to(file_path).await?
} }
nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&mut conn).await, &mut conn) nt.send_send_update(
.await; UpdateType::SyncSendCreate,
&send,
&send.update_users_revision(&mut conn).await,
&headers.device.uuid,
&mut conn,
)
.await;
} else { } else {
err!("Send not found. Unable to save the file."); err!("Send not found. Unable to save the file.");
} }
@ -356,6 +374,7 @@ pub struct SendAccessData {
async fn post_access( async fn post_access(
access_id: &str, access_id: &str,
data: JsonUpcase<SendAccessData>, data: JsonUpcase<SendAccessData>,
headers: Headers,
mut conn: DbConn, mut conn: DbConn,
ip: ClientIp, ip: ClientIp,
nt: Notify<'_>, nt: Notify<'_>,
@ -400,8 +419,14 @@ async fn post_access(
send.save(&mut conn).await?; send.save(&mut conn).await?;
nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await, &mut conn) nt.send_send_update(
.await; UpdateType::SyncSendUpdate,
&send,
&send.update_users_revision(&mut conn).await,
&headers.device.uuid,
&mut conn,
)
.await;
Ok(Json(send.to_json_access(&mut conn).await)) Ok(Json(send.to_json_access(&mut conn).await))
} }
@ -412,6 +437,7 @@ async fn post_access_file(
file_id: &str, file_id: &str,
data: JsonUpcase<SendAccessData>, data: JsonUpcase<SendAccessData>,
host: Host, host: Host,
headers: Headers,
mut conn: DbConn, mut conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
@ -452,8 +478,14 @@ async fn post_access_file(
send.save(&mut conn).await?; send.save(&mut conn).await?;
nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await, &mut conn) nt.send_send_update(
.await; UpdateType::SyncSendUpdate,
&send,
&send.update_users_revision(&mut conn).await,
&headers.device.uuid,
&mut conn,
)
.await;
let token_claims = crate::auth::generate_send_claims(send_id, file_id); let token_claims = crate::auth::generate_send_claims(send_id, file_id);
let token = crate::auth::encode_jwt(&token_claims); let token = crate::auth::encode_jwt(&token_claims);
@ -535,8 +567,14 @@ async fn put_send(
} }
send.save(&mut conn).await?; send.save(&mut conn).await?;
nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await, &mut conn) nt.send_send_update(
.await; UpdateType::SyncSendUpdate,
&send,
&send.update_users_revision(&mut conn).await,
&headers.device.uuid,
&mut conn,
)
.await;
Ok(Json(send.to_json())) Ok(Json(send.to_json()))
} }
@ -553,8 +591,14 @@ async fn delete_send(id: &str, headers: Headers, mut conn: DbConn, nt: Notify<'_
} }
send.delete(&mut conn).await?; send.delete(&mut conn).await?;
nt.send_send_update(UpdateType::SyncSendDelete, &send, &send.update_users_revision(&mut conn).await, &mut conn) nt.send_send_update(
.await; UpdateType::SyncSendDelete,
&send,
&send.update_users_revision(&mut conn).await,
&headers.device.uuid,
&mut conn,
)
.await;
Ok(()) Ok(())
} }
@ -574,8 +618,14 @@ async fn put_remove_password(id: &str, headers: Headers, mut conn: DbConn, nt: N
send.set_password(None); send.set_password(None);
send.save(&mut conn).await?; send.save(&mut conn).await?;
nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await, &mut conn) nt.send_send_update(
.await; UpdateType::SyncSendUpdate,
&send,
&send.update_users_revision(&mut conn).await,
&headers.device.uuid,
&mut conn,
)
.await;
Ok(Json(send.to_json())) Ok(Json(send.to_json()))
} }

View File

@ -240,11 +240,11 @@ impl WebSocketUsers {
self.send_update(&user.uuid, &data).await; self.send_update(&user.uuid, &data).await;
if CONFIG.push_enabled() { if CONFIG.push_enabled() {
push_user_update(ut, user).await; push_user_update(ut, user);
} }
} }
pub async fn send_logout(&self, user: &User, acting_device_uuid: Option<String>, conn: &mut DbConn) { pub async fn send_logout(&self, user: &User, acting_device_uuid: Option<String>) {
let data = create_update( let data = create_update(
vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))], vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))],
UpdateType::LogOut, UpdateType::LogOut,
@ -254,7 +254,7 @@ impl WebSocketUsers {
self.send_update(&user.uuid, &data).await; self.send_update(&user.uuid, &data).await;
if CONFIG.push_enabled() { if CONFIG.push_enabled() {
push_logout(user, acting_device_uuid, conn).await; push_logout(user, acting_device_uuid);
} }
} }
@ -325,7 +325,14 @@ impl WebSocketUsers {
} }
} }
pub async fn send_send_update(&self, ut: UpdateType, send: &DbSend, user_uuids: &[String], conn: &mut DbConn) { pub async fn send_send_update(
&self,
ut: UpdateType,
send: &DbSend,
user_uuids: &[String],
acting_device_uuid: &String,
conn: &mut DbConn,
) {
let user_uuid = convert_option(send.user_uuid.clone()); let user_uuid = convert_option(send.user_uuid.clone());
let data = create_update( let data = create_update(
@ -342,7 +349,7 @@ impl WebSocketUsers {
self.send_update(uuid, &data).await; self.send_update(uuid, &data).await;
} }
if CONFIG.push_enabled() && user_uuids.len() == 1 { if CONFIG.push_enabled() && user_uuids.len() == 1 {
push_send_update(ut, send, conn).await; push_send_update(ut, send, acting_device_uuid, conn).await;
} }
} }
} }

View File

@ -139,71 +139,52 @@ pub async fn push_cipher_update(
} }
}; };
for device in Device::find_by_user(user_uuid, conn).await { if Device::check_user_has_push_device(user_uuid, conn).await {
let data = json!({ send_to_push_relay(json!({
"userId": user_uuid, "userId": user_uuid,
"organizationId": (), "organizationId": (),
"deviceId": device.push_uuid, "deviceId": acting_device_uuid,
"identifier": acting_device_uuid, "identifier": acting_device_uuid,
"type": ut as i32, "type": ut as i32,
"payload": { "payload": {
"Id": cipher.uuid, "id": cipher.uuid,
"UserId": cipher.user_uuid, "userId": cipher.user_uuid,
"OrganizationId": (),
"RevisionDate": cipher.updated_at
}
});
send_to_push_relay(data).await;
}
}
pub async fn push_logout(user: &User, acting_device_uuid: Option<String>, conn: &mut crate::db::DbConn) {
if let Some(d) = acting_device_uuid {
for device in Device::find_by_user(&user.uuid, conn).await {
let data = json!({
"userId": user.uuid,
"organizationId": (), "organizationId": (),
"deviceId": device.push_uuid, "revisionDate": cipher.updated_at
"identifier": d,
"type": UpdateType::LogOut as i32,
"payload": {
"UserId": user.uuid,
"Date": user.updated_at
}
});
send_to_push_relay(data).await;
}
} else {
let data = json!({
"userId": user.uuid,
"organizationId": (),
"deviceId": (),
"identifier": (),
"type": UpdateType::LogOut as i32,
"payload": {
"UserId": user.uuid,
"Date": user.updated_at
} }
}); }))
send_to_push_relay(data).await; .await;
} }
} }
pub async fn push_user_update(ut: UpdateType, user: &User) { pub fn push_logout(user: &User, acting_device_uuid: Option<String>) {
let data = json!({ let acting_device_uuid: Value = acting_device_uuid.map(|v| v.into()).unwrap_or_else(|| Value::Null);
tokio::task::spawn(send_to_push_relay(json!({
"userId": user.uuid,
"organizationId": (),
"deviceId": acting_device_uuid,
"identifier": acting_device_uuid,
"type": UpdateType::LogOut as i32,
"payload": {
"userId": user.uuid,
"date": user.updated_at
}
})));
}
pub fn push_user_update(ut: UpdateType, user: &User) {
tokio::task::spawn(send_to_push_relay(json!({
"userId": user.uuid, "userId": user.uuid,
"organizationId": (), "organizationId": (),
"deviceId": (), "deviceId": (),
"identifier": (), "identifier": (),
"type": ut as i32, "type": ut as i32,
"payload": { "payload": {
"UserId": user.uuid, "userId": user.uuid,
"Date": user.updated_at "date": user.updated_at
} }
}); })));
send_to_push_relay(data).await;
} }
pub async fn push_folder_update( pub async fn push_folder_update(
@ -212,46 +193,42 @@ pub async fn push_folder_update(
acting_device_uuid: &String, acting_device_uuid: &String,
conn: &mut crate::db::DbConn, conn: &mut crate::db::DbConn,
) { ) {
for device in Device::find_by_user(&folder.user_uuid, conn).await { if Device::check_user_has_push_device(&folder.user_uuid, conn).await {
let data = json!({ tokio::task::spawn(send_to_push_relay(json!({
"userId": folder.user_uuid, "userId": folder.user_uuid,
"organizationId": (), "organizationId": (),
"deviceId": device.push_uuid, "deviceId": acting_device_uuid,
"identifier": acting_device_uuid, "identifier": acting_device_uuid,
"type": ut as i32, "type": ut as i32,
"payload": { "payload": {
"Id": folder.uuid, "id": folder.uuid,
"UserId": folder.user_uuid, "userId": folder.user_uuid,
"RevisionDate": folder.updated_at "revisionDate": folder.updated_at
} }
}); })));
send_to_push_relay(data).await;
} }
} }
pub async fn push_send_update(ut: UpdateType, send: &Send, conn: &mut crate::db::DbConn) { pub async fn push_send_update(ut: UpdateType, send: &Send, acting_device_uuid: &String, conn: &mut crate::db::DbConn) {
if let Some(s) = &send.user_uuid { if let Some(s) = &send.user_uuid {
for device in Device::find_by_user(s, conn).await { if Device::check_user_has_push_device(s, conn).await {
let data = json!({ tokio::task::spawn(send_to_push_relay(json!({
"userId": send.user_uuid, "userId": send.user_uuid,
"organizationId": (), "organizationId": (),
"deviceId": device.push_uuid, "deviceId": acting_device_uuid,
"identifier": (), "identifier": acting_device_uuid,
"type": ut as i32, "type": ut as i32,
"payload": { "payload": {
"Id": send.uuid, "id": send.uuid,
"UserId": send.user_uuid, "userId": send.user_uuid,
"RevisionDate": send.revision_date "revisionDate": send.revision_date
} }
}); })));
send_to_push_relay(data).await;
} }
} }
} }
async fn send_to_push_relay(data: Value) { async fn send_to_push_relay(notification_data: Value) {
if !CONFIG.push_enabled() { if !CONFIG.push_enabled() {
return; return;
} }
@ -270,8 +247,8 @@ async fn send_to_push_relay(data: Value) {
.post(CONFIG.push_relay_uri() + "/push/send") .post(CONFIG.push_relay_uri() + "/push/send")
.header(ACCEPT, "application/json") .header(ACCEPT, "application/json")
.header(CONTENT_TYPE, "application/json") .header(CONTENT_TYPE, "application/json")
.header(AUTHORIZATION, auth_header) .header(AUTHORIZATION, &auth_header)
.json(&data) .json(&notification_data)
.send() .send()
.await .await
{ {

View File

@ -202,7 +202,7 @@ impl Device {
.from_db() .from_db()
}} }}
} }
pub async fn find_push_device_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> { pub async fn find_push_devices_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
devices::table devices::table
.filter(devices::user_uuid.eq(user_uuid)) .filter(devices::user_uuid.eq(user_uuid))
@ -212,4 +212,16 @@ impl Device {
.from_db() .from_db()
}} }}
} }
pub async fn check_user_has_push_device(user_uuid: &str, conn: &mut DbConn) -> bool {
db_run! { conn: {
devices::table
.filter(devices::user_uuid.eq(user_uuid))
.filter(devices::push_token.is_not_null())
.count()
.first::<i64>(conn)
.ok()
.unwrap_or(0) != 0
}}
}
} }