commit e4606431d1f79133fe7d708736b4083e9adb8a01
parent 5b7d7390b0cc26ebde96d1b8a835e384ee8deb47
Author: Bernd Schoolmann <mail@quexten.com>
Date: Fri, 16 Jun 2023 23:34:16 +0200
Fix mobile push blocking requests and spamming push server
Diffstat:
7 files changed, 146 insertions(+), 100 deletions(-)
diff --git a/src/api/admin.rs b/src/api/admin.rs
@@ -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 {
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() {
- 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 {
Ok(r) => r,
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;
- nt.send_logout(&user, None, &mut conn).await;
+ nt.send_logout(&user, None).await;
save_result
}
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
@@ -343,7 +343,7 @@ async fn post_password(
// 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.
// 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
}
@@ -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);
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
}
@@ -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.
// If you do logout the user it will causes issues at the client side.
// 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
}
@@ -513,7 +513,7 @@ async fn post_sstamp(
user.reset_security_stamp();
let save_result = user.save(&mut conn).await;
- nt.send_logout(&user, None, &mut conn).await;
+ nt.send_logout(&user, None).await;
save_result
}
@@ -616,7 +616,7 @@ async fn post_email(
let save_result = user.save(&mut conn).await;
- nt.send_logout(&user, None, &mut conn).await;
+ nt.send_logout(&user, None).await;
save_result
}
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
@@ -2718,7 +2718,7 @@ async fn put_reset_password(
user.set_password(reset_request.NewMasterPasswordHash.as_str(), Some(reset_request.Key), true, None);
user.save(&mut conn).await?;
- nt.send_logout(&user, None, &mut conn).await;
+ nt.send_logout(&user, None).await;
log_event(
EventType::OrganizationUserAdminResetPassword as i32,
diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs
@@ -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)?;
send.save(&mut conn).await?;
- nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&mut conn).await, &mut conn)
- .await;
+ nt.send_send_update(
+ UpdateType::SyncSendCreate,
+ &send,
+ &send.update_users_revision(&mut conn).await,
+ &headers.device.uuid,
+ &mut conn,
+ )
+ .await;
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
send.save(&mut conn).await?;
- nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&mut conn).await, &mut conn)
- .await;
+ nt.send_send_update(
+ UpdateType::SyncSendCreate,
+ &send,
+ &send.update_users_revision(&mut conn).await,
+ &headers.device.uuid,
+ &mut conn,
+ )
+ .await;
Ok(Json(send.to_json()))
}
@@ -337,8 +349,14 @@ async fn post_send_file_v2_data(
data.data.move_copy_to(file_path).await?
}
- nt.send_send_update(UpdateType::SyncSendCreate, &send, &send.update_users_revision(&mut conn).await, &mut conn)
- .await;
+ nt.send_send_update(
+ UpdateType::SyncSendCreate,
+ &send,
+ &send.update_users_revision(&mut conn).await,
+ &headers.device.uuid,
+ &mut conn,
+ )
+ .await;
} else {
err!("Send not found. Unable to save the file.");
}
@@ -356,6 +374,7 @@ pub struct SendAccessData {
async fn post_access(
access_id: &str,
data: JsonUpcase<SendAccessData>,
+ headers: Headers,
mut conn: DbConn,
ip: ClientIp,
nt: Notify<'_>,
@@ -400,8 +419,14 @@ async fn post_access(
send.save(&mut conn).await?;
- nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await, &mut conn)
- .await;
+ nt.send_send_update(
+ 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))
}
@@ -412,6 +437,7 @@ async fn post_access_file(
file_id: &str,
data: JsonUpcase<SendAccessData>,
host: Host,
+ headers: Headers,
mut conn: DbConn,
nt: Notify<'_>,
) -> JsonResult {
@@ -452,8 +478,14 @@ async fn post_access_file(
send.save(&mut conn).await?;
- nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await, &mut conn)
- .await;
+ nt.send_send_update(
+ 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 = crate::auth::encode_jwt(&token_claims);
@@ -535,8 +567,14 @@ async fn put_send(
}
send.save(&mut conn).await?;
- nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await, &mut conn)
- .await;
+ nt.send_send_update(
+ UpdateType::SyncSendUpdate,
+ &send,
+ &send.update_users_revision(&mut conn).await,
+ &headers.device.uuid,
+ &mut conn,
+ )
+ .await;
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?;
- nt.send_send_update(UpdateType::SyncSendDelete, &send, &send.update_users_revision(&mut conn).await, &mut conn)
- .await;
+ nt.send_send_update(
+ UpdateType::SyncSendDelete,
+ &send,
+ &send.update_users_revision(&mut conn).await,
+ &headers.device.uuid,
+ &mut conn,
+ )
+ .await;
Ok(())
}
@@ -574,8 +618,14 @@ async fn put_remove_password(id: &str, headers: Headers, mut conn: DbConn, nt: N
send.set_password(None);
send.save(&mut conn).await?;
- nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await, &mut conn)
- .await;
+ nt.send_send_update(
+ UpdateType::SyncSendUpdate,
+ &send,
+ &send.update_users_revision(&mut conn).await,
+ &headers.device.uuid,
+ &mut conn,
+ )
+ .await;
Ok(Json(send.to_json()))
}
diff --git a/src/api/notifications.rs b/src/api/notifications.rs
@@ -240,11 +240,11 @@ impl WebSocketUsers {
self.send_update(&user.uuid, &data).await;
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(
vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))],
UpdateType::LogOut,
@@ -254,7 +254,7 @@ impl WebSocketUsers {
self.send_update(&user.uuid, &data).await;
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 data = create_update(
@@ -342,7 +349,7 @@ impl WebSocketUsers {
self.send_update(uuid, &data).await;
}
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;
}
}
}
diff --git a/src/api/push.rs b/src/api/push.rs
@@ -139,71 +139,52 @@ pub async fn push_cipher_update(
}
};
- for device in Device::find_by_user(user_uuid, conn).await {
- let data = json!({
+ if Device::check_user_has_push_device(user_uuid, conn).await {
+ send_to_push_relay(json!({
"userId": user_uuid,
"organizationId": (),
- "deviceId": device.push_uuid,
+ "deviceId": acting_device_uuid,
"identifier": acting_device_uuid,
"type": ut as i32,
"payload": {
- "Id": cipher.uuid,
- "UserId": cipher.user_uuid,
- "OrganizationId": (),
- "RevisionDate": cipher.updated_at
+ "id": cipher.uuid,
+ "userId": cipher.user_uuid,
+ "organizationId": (),
+ "revisionDate": cipher.updated_at
}
- });
-
- send_to_push_relay(data).await;
+ }))
+ .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": (),
- "deviceId": device.push_uuid,
- "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!({
+pub fn push_logout(user: &User, acting_device_uuid: Option<String>) {
+ 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,
- "organizationId": (),
- "deviceId": (),
- "identifier": (),
- "type": UpdateType::LogOut as i32,
- "payload": {
- "UserId": user.uuid,
- "Date": user.updated_at
- }
- });
- send_to_push_relay(data).await;
- }
+ "date": user.updated_at
+ }
+ })));
}
-pub async fn push_user_update(ut: UpdateType, user: &User) {
- let data = json!({
+pub fn push_user_update(ut: UpdateType, user: &User) {
+ tokio::task::spawn(send_to_push_relay(json!({
"userId": user.uuid,
"organizationId": (),
"deviceId": (),
"identifier": (),
"type": ut as i32,
"payload": {
- "UserId": user.uuid,
- "Date": user.updated_at
+ "userId": user.uuid,
+ "date": user.updated_at
}
- });
-
- send_to_push_relay(data).await;
+ })));
}
pub async fn push_folder_update(
@@ -212,46 +193,42 @@ pub async fn push_folder_update(
acting_device_uuid: &String,
conn: &mut crate::db::DbConn,
) {
- for device in Device::find_by_user(&folder.user_uuid, conn).await {
- let data = json!({
+ if Device::check_user_has_push_device(&folder.user_uuid, conn).await {
+ tokio::task::spawn(send_to_push_relay(json!({
"userId": folder.user_uuid,
"organizationId": (),
- "deviceId": device.push_uuid,
+ "deviceId": acting_device_uuid,
"identifier": acting_device_uuid,
"type": ut as i32,
"payload": {
- "Id": folder.uuid,
- "UserId": folder.user_uuid,
- "RevisionDate": folder.updated_at
+ "id": folder.uuid,
+ "userId": folder.user_uuid,
+ "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 {
- for device in Device::find_by_user(s, conn).await {
- let data = json!({
+ if Device::check_user_has_push_device(s, conn).await {
+ tokio::task::spawn(send_to_push_relay(json!({
"userId": send.user_uuid,
"organizationId": (),
- "deviceId": device.push_uuid,
- "identifier": (),
+ "deviceId": acting_device_uuid,
+ "identifier": acting_device_uuid,
"type": ut as i32,
"payload": {
- "Id": send.uuid,
- "UserId": send.user_uuid,
- "RevisionDate": send.revision_date
+ "id": send.uuid,
+ "userId": send.user_uuid,
+ "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() {
return;
}
@@ -270,8 +247,8 @@ async fn send_to_push_relay(data: Value) {
.post(CONFIG.push_relay_uri() + "/push/send")
.header(ACCEPT, "application/json")
.header(CONTENT_TYPE, "application/json")
- .header(AUTHORIZATION, auth_header)
- .json(&data)
+ .header(AUTHORIZATION, &auth_header)
+ .json(¬ification_data)
.send()
.await
{
diff --git a/src/db/models/device.rs b/src/db/models/device.rs
@@ -202,7 +202,7 @@ impl Device {
.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: {
devices::table
.filter(devices::user_uuid.eq(user_uuid))
@@ -212,4 +212,16 @@ impl Device {
.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
+ }}
+ }
}