feat: Push Notifications

Co-authored-by: samb-devel <125741162+samb-devel@users.noreply.github.com>
Co-authored-by: Zoruk <Zoruk@users.noreply.github.com>
This commit is contained in:
GeekCornerGH
2023-06-11 13:28:18 +02:00
parent adf67a8ee8
commit 2d66292350
22 changed files with 526 additions and 70 deletions

View File

@@ -4,7 +4,8 @@ use serde_json::Value;
use crate::{
api::{
core::log_user_event, EmptyResult, JsonResult, JsonUpcase, Notify, NumberOrString, PasswordData, UpdateType,
core::log_user_event, register_push_device, unregister_push_device, EmptyResult, JsonResult, JsonUpcase,
Notify, NumberOrString, PasswordData, UpdateType,
},
auth::{decode_delete, decode_invite, decode_verify_email, Headers},
crypto,
@@ -35,6 +36,7 @@ pub fn routes() -> Vec<rocket::Route> {
post_verify_email_token,
post_delete_recover,
post_delete_recover_token,
post_device_token,
delete_account,
post_delete_account,
revision_date,
@@ -46,6 +48,9 @@ pub fn routes() -> Vec<rocket::Route> {
get_known_device,
get_known_device_from_path,
put_avatar,
put_device_token,
put_clear_device_token,
post_clear_device_token,
]
}
@@ -338,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)).await;
nt.send_logout(&user, Some(headers.device.uuid), &mut conn).await;
save_result
}
@@ -398,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)).await;
nt.send_logout(&user, Some(headers.device.uuid), &mut conn).await;
save_result
}
@@ -485,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)).await;
nt.send_logout(&user, Some(headers.device.uuid), &mut conn).await;
save_result
}
@@ -508,7 +513,7 @@ async fn post_sstamp(
user.reset_security_stamp();
let save_result = user.save(&mut conn).await;
nt.send_logout(&user, None).await;
nt.send_logout(&user, None, &mut conn).await;
save_result
}
@@ -611,7 +616,7 @@ async fn post_email(
let save_result = user.save(&mut conn).await;
nt.send_logout(&user, None).await;
nt.send_logout(&user, None, &mut conn).await;
save_result
}
@@ -930,3 +935,64 @@ impl<'r> FromRequest<'r> for KnownDevice {
})
}
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct PushToken {
PushToken: String,
}
#[post("/devices/identifier/<uuid>/token", data = "<data>")]
async fn post_device_token(uuid: &str, data: JsonUpcase<PushToken>, headers: Headers, conn: DbConn) -> EmptyResult {
put_device_token(uuid, data, headers, conn).await
}
#[put("/devices/identifier/<uuid>/token", data = "<data>")]
async fn put_device_token(uuid: &str, data: JsonUpcase<PushToken>, headers: Headers, mut conn: DbConn) -> EmptyResult {
if !CONFIG.push_enabled() {
return Ok(());
}
let data = data.into_inner().data;
let token = data.PushToken;
let mut device = match Device::find_by_uuid_and_user(&headers.device.uuid, &headers.user.uuid, &mut conn).await {
Some(device) => device,
None => err!(format!("Error: device {uuid} should be present before a token can be assigned")),
};
device.push_token = Some(token);
if device.push_uuid.is_none() {
device.push_uuid = Some(uuid::Uuid::new_v4().to_string());
}
if let Err(e) = device.save(&mut conn).await {
err!(format!("An error occured while trying to save the device push token: {e}"));
}
if let Err(e) = register_push_device(headers.user.uuid, device).await {
err!(format!("An error occured while proceeding registration of a device: {e}"));
}
Ok(())
}
#[put("/devices/identifier/<uuid>/clear-token")]
async fn put_clear_device_token(uuid: &str, mut conn: DbConn) -> EmptyResult {
// This only clears push token
// https://github.com/bitwarden/core/blob/master/src/Api/Controllers/DevicesController.cs#L109
// https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37
// This is somehow not implemented in any app, added it in case it is required
if !CONFIG.push_enabled() {
return Ok(());
}
if let Some(device) = Device::find_by_uuid(uuid, &mut conn).await {
Device::clear_push_token_by_uuid(uuid, &mut conn).await?;
unregister_push_device(device.uuid).await?;
}
Ok(())
}
// On upstream server, both PUT and POST are declared. Implementing the POST method in case it would be useful somewhere
#[post("/devices/identifier/<uuid>/clear-token")]
async fn post_clear_device_token(uuid: &str, conn: DbConn) -> EmptyResult {
put_clear_device_token(uuid, conn).await
}