commit 05a552910cc22f0af94a5609859ea1a547328b0d
parent 193c2fa86064304c2682ac5b0d65a155385af368
Author: Daniel GarcĂa <dani-garcia@users.noreply.github.com>
Date: Mon, 9 Jan 2023 18:24:00 +0100
Merge branch 'BlackDex-update-notifications'
Diffstat:
8 files changed, 188 insertions(+), 64 deletions(-)
diff --git a/src/api/admin.rs b/src/api/admin.rs
@@ -13,7 +13,7 @@ use rocket::{
};
use crate::{
- api::{core::log_event, ApiResult, EmptyResult, JsonResult, NumberOrString},
+ api::{core::log_event, ApiResult, EmptyResult, JsonResult, Notify, NumberOrString, UpdateType},
auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp},
config::ConfigBuilder,
db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
@@ -365,22 +365,30 @@ async fn delete_user(uuid: String, _token: AdminToken, mut conn: DbConn, ip: Cli
}
#[post("/users/<uuid>/deauth")]
-async fn deauth_user(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
+async fn deauth_user(uuid: String, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?;
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
user.reset_security_stamp();
- user.save(&mut conn).await
+ let save_result = user.save(&mut conn).await;
+
+ nt.send_user_update(UpdateType::LogOut, &user).await;
+
+ save_result
}
#[post("/users/<uuid>/disable")]
-async fn disable_user(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
+async fn disable_user(uuid: String, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?;
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
user.reset_security_stamp();
user.enabled = false;
- user.save(&mut conn).await
+ let save_result = user.save(&mut conn).await;
+
+ nt.send_user_update(UpdateType::LogOut, &user).await;
+
+ save_result
}
#[post("/users/<uuid>/enable")]
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
@@ -275,6 +275,7 @@ async fn post_password(
headers: Headers,
mut conn: DbConn,
ip: ClientIp,
+ nt: Notify<'_>,
) -> EmptyResult {
let data: ChangePassData = data.into_inner().data;
let mut user = headers.user;
@@ -293,7 +294,11 @@ async fn post_password(
Some(vec![String::from("post_rotatekey"), String::from("get_contacts"), String::from("get_public_keys")]),
);
user.akey = data.Key;
- user.save(&mut conn).await
+ let save_result = user.save(&mut conn).await;
+
+ nt.send_user_update(UpdateType::LogOut, &user).await;
+
+ save_result
}
#[derive(Deserialize)]
@@ -308,7 +313,7 @@ struct ChangeKdfData {
}
#[post("/accounts/kdf", data = "<data>")]
-async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
+async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let data: ChangeKdfData = data.into_inner().data;
let mut user = headers.user;
@@ -320,7 +325,11 @@ async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: D
user.client_kdf_type = data.Kdf;
user.set_password(&data.NewMasterPasswordHash, None);
user.akey = data.Key;
- user.save(&mut conn).await
+ let save_result = user.save(&mut conn).await;
+
+ nt.send_user_update(UpdateType::LogOut, &user).await;
+
+ save_result
}
#[derive(Deserialize)]
@@ -388,6 +397,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)
.await?
}
@@ -399,11 +409,20 @@ async fn post_rotatekey(
user.private_key = Some(data.PrivateKey);
user.reset_security_stamp();
- user.save(&mut conn).await
+ let save_result = user.save(&mut conn).await;
+
+ nt.send_user_update(UpdateType::LogOut, &user).await;
+
+ save_result
}
#[post("/accounts/security-stamp", data = "<data>")]
-async fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
+async fn post_sstamp(
+ data: JsonUpcase<PasswordData>,
+ headers: Headers,
+ mut conn: DbConn,
+ nt: Notify<'_>,
+) -> EmptyResult {
let data: PasswordData = data.into_inner().data;
let mut user = headers.user;
@@ -413,7 +432,11 @@ async fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, mut conn:
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
user.reset_security_stamp();
- user.save(&mut conn).await
+ let save_result = user.save(&mut conn).await;
+
+ nt.send_user_update(UpdateType::LogOut, &user).await;
+
+ save_result
}
#[derive(Deserialize)]
@@ -465,7 +488,12 @@ struct ChangeEmailData {
}
#[post("/accounts/email", data = "<data>")]
-async fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
+async fn post_email(
+ data: JsonUpcase<ChangeEmailData>,
+ headers: Headers,
+ mut conn: DbConn,
+ nt: Notify<'_>,
+) -> EmptyResult {
let data: ChangeEmailData = data.into_inner().data;
let mut user = headers.user;
@@ -507,8 +535,11 @@ async fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, mut con
user.set_password(&data.NewMasterPasswordHash, None);
user.akey = data.Key;
+ let save_result = user.save(&mut conn).await;
- user.save(&mut conn).await
+ nt.send_user_update(UpdateType::LogOut, &user).await;
+
+ save_result
}
#[post("/accounts/verify-email")]
diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs
@@ -310,7 +310,8 @@ 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::CipherCreate).await?;
+ update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherCreate)
+ .await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await))
}
@@ -415,7 +416,14 @@ pub async fn update_cipher_from_data(
for (id, attachment) in attachments {
let mut saved_att = match Attachment::find_by_id(&id, conn).await {
Some(att) => att,
- None => err!("Attachment doesn't exist"),
+ None => {
+ // Warn and continue here.
+ // A missing attachment means it was removed via an other client.
+ // Also the Desktop Client supports removing attachments and save an update afterwards.
+ // Bitwarden it self ignores these mismatches server side.
+ warn!("Attachment {id} doesn't exist");
+ continue;
+ }
};
if saved_att.cipher_uuid != cipher.uuid {
@@ -482,8 +490,8 @@ pub async fn update_cipher_from_data(
// Only log events for organizational ciphers
if let Some(org_uuid) = &cipher.organization_uuid {
let event_type = match (&ut, transfer_cipher) {
- (UpdateType::CipherCreate, true) => EventType::CipherCreated,
- (UpdateType::CipherUpdate, true) => EventType::CipherShared,
+ (UpdateType::SyncCipherCreate, true) => EventType::CipherCreated,
+ (UpdateType::SyncCipherUpdate, true) => EventType::CipherShared,
(_, _) => EventType::CipherUpdated,
};
@@ -499,7 +507,7 @@ pub async fn update_cipher_from_data(
.await;
}
- nt.send_cipher_update(ut, cipher, &cipher.update_users_revision(conn).await).await;
+ nt.send_cipher_update(ut, cipher, &cipher.update_users_revision(conn).await, &headers.device.uuid).await;
}
Ok(())
@@ -562,7 +570,7 @@ async fn post_ciphers_import(
let mut user = headers.user;
user.update_revision(&mut conn).await?;
- nt.send_user_update(UpdateType::Vault, &user).await;
+ nt.send_user_update(UpdateType::SyncVault, &user).await;
Ok(())
}
@@ -628,7 +636,8 @@ async fn put_cipher(
err!("Cipher is not write accessible")
}
- update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::CipherUpdate).await?;
+ update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherUpdate)
+ .await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await))
}
@@ -850,9 +859,9 @@ async fn share_cipher_by_uuid(
// When LastKnownRevisionDate is None, it is a new cipher, so send CipherCreate.
let ut = if data.Cipher.LastKnownRevisionDate.is_some() {
- UpdateType::CipherUpdate
+ UpdateType::SyncCipherUpdate
} else {
- UpdateType::CipherCreate
+ UpdateType::SyncCipherCreate
};
update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, ip, nt, ut).await?;
@@ -1054,7 +1063,13 @@ async fn save_attachment(
data.data.move_copy_to(file_path).await?
}
- nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&mut conn).await).await;
+ nt.send_cipher_update(
+ UpdateType::SyncCipherUpdate,
+ &cipher,
+ &cipher.update_users_revision(&mut conn).await,
+ &headers.device.uuid,
+ )
+ .await;
if let Some(org_uuid) = &cipher.organization_uuid {
log_event(
@@ -1390,7 +1405,7 @@ async fn move_cipher_selected(
// Move cipher
cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &mut conn).await?;
- nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &[user_uuid.clone()]).await;
+ nt.send_cipher_update(UpdateType::SyncCipherUpdate, &cipher, &[user_uuid.clone()], &headers.device.uuid).await;
}
Ok(())
@@ -1438,7 +1453,7 @@ async fn delete_all(
Some(user_org) => {
if user_org.atype == UserOrgType::Owner {
Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?;
- nt.send_user_update(UpdateType::Vault, &user).await;
+ nt.send_user_update(UpdateType::SyncVault, &user).await;
log_event(
EventType::OrganizationPurgedVault as i32,
@@ -1471,7 +1486,7 @@ async fn delete_all(
}
user.update_revision(&mut conn).await?;
- nt.send_user_update(UpdateType::Vault, &user).await;
+ nt.send_user_update(UpdateType::SyncVault, &user).await;
Ok(())
}
}
@@ -1497,10 +1512,22 @@ async fn _delete_cipher_by_uuid(
if soft_delete {
cipher.deleted_at = Some(Utc::now().naive_utc());
cipher.save(conn).await?;
- nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await).await;
+ nt.send_cipher_update(
+ UpdateType::SyncCipherUpdate,
+ &cipher,
+ &cipher.update_users_revision(conn).await,
+ &headers.device.uuid,
+ )
+ .await;
} else {
cipher.delete(conn).await?;
- nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(conn).await).await;
+ nt.send_cipher_update(
+ UpdateType::SyncCipherDelete,
+ &cipher,
+ &cipher.update_users_revision(conn).await,
+ &headers.device.uuid,
+ )
+ .await;
}
if let Some(org_uuid) = cipher.organization_uuid {
@@ -1562,7 +1589,13 @@ async fn _restore_cipher_by_uuid(
cipher.deleted_at = None;
cipher.save(conn).await?;
- nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await).await;
+ nt.send_cipher_update(
+ UpdateType::SyncCipherUpdate,
+ &cipher,
+ &cipher.update_users_revision(conn).await,
+ &headers.device.uuid,
+ )
+ .await;
if let Some(org_uuid) = &cipher.organization_uuid {
log_event(
EventType::CipherRestored as i32,
@@ -1639,7 +1672,13 @@ async fn _delete_cipher_attachment_by_id(
// Delete attachment
attachment.delete(conn).await?;
- nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await).await;
+ nt.send_cipher_update(
+ UpdateType::SyncCipherUpdate,
+ &cipher,
+ &cipher.update_users_revision(conn).await,
+ &headers.device.uuid,
+ )
+ .await;
if let Some(org_uuid) = cipher.organization_uuid {
log_event(
EventType::CipherAttachmentDeleted as i32,
diff --git a/src/api/core/folders.rs b/src/api/core/folders.rs
@@ -50,7 +50,7 @@ async fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, mut conn:
let mut folder = Folder::new(headers.user.uuid, data.Name);
folder.save(&mut conn).await?;
- nt.send_folder_update(UpdateType::FolderCreate, &folder).await;
+ nt.send_folder_update(UpdateType::SyncFolderCreate, &folder, &headers.device.uuid).await;
Ok(Json(folder.to_json()))
}
@@ -88,7 +88,7 @@ async fn put_folder(
folder.name = data.Name;
folder.save(&mut conn).await?;
- nt.send_folder_update(UpdateType::FolderUpdate, &folder).await;
+ nt.send_folder_update(UpdateType::SyncFolderUpdate, &folder, &headers.device.uuid).await;
Ok(Json(folder.to_json()))
}
@@ -112,6 +112,6 @@ async fn delete_folder(uuid: String, headers: Headers, mut conn: DbConn, nt: Not
// Delete the actual folder entry
folder.delete(&mut conn).await?;
- nt.send_folder_update(UpdateType::FolderDelete, &folder).await;
+ nt.send_folder_update(UpdateType::SyncFolderDelete, &folder, &headers.device.uuid).await;
Ok(())
}
diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs
@@ -7,8 +7,7 @@ mod organizations;
mod sends;
pub mod two_factor;
-pub use ciphers::purge_trashed_ciphers;
-pub use ciphers::{CipherSyncData, CipherSyncType};
+pub use ciphers::{purge_trashed_ciphers, CipherSyncData, CipherSyncType};
pub use emergency_access::{emergency_notification_reminder_job, emergency_request_timeout_job};
pub use events::{event_cleanup_job, log_event, log_user_event};
pub use sends::purge_sends;
@@ -47,13 +46,11 @@ pub fn events_routes() -> Vec<Route> {
//
// Move this somewhere else
//
-use rocket::serde::json::Json;
-use rocket::Catcher;
-use rocket::Route;
+use rocket::{serde::json::Json, Catcher, Route};
use serde_json::Value;
use crate::{
- api::{JsonResult, JsonUpcase},
+ api::{JsonResult, JsonUpcase, Notify, UpdateType},
auth::Headers,
db::DbConn,
error::Error,
@@ -138,7 +135,12 @@ struct EquivDomainData {
}
#[post("/settings/domains", data = "<data>")]
-async fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, mut conn: DbConn) -> JsonResult {
+async fn post_eq_domains(
+ data: JsonUpcase<EquivDomainData>,
+ headers: Headers,
+ mut conn: DbConn,
+ nt: Notify<'_>,
+) -> JsonResult {
let data: EquivDomainData = data.into_inner().data;
let excluded_globals = data.ExcludedGlobalEquivalentDomains.unwrap_or_default();
@@ -152,12 +154,19 @@ async fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, mu
user.save(&mut conn).await?;
+ nt.send_user_update(UpdateType::SyncSettings, &user).await;
+
Ok(Json(json!({})))
}
#[put("/settings/domains", data = "<data>")]
-async fn put_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbConn) -> JsonResult {
- post_eq_domains(data, headers, conn).await
+async fn put_eq_domains(
+ data: JsonUpcase<EquivDomainData>,
+ headers: Headers,
+ conn: DbConn,
+ nt: Notify<'_>,
+) -> JsonResult {
+ post_eq_domains(data, headers, conn, nt).await
}
#[get("/hibp/breach?<username>")]
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
@@ -957,6 +957,7 @@ async fn bulk_confirm_invite(
headers: AdminHeaders,
mut conn: DbConn,
ip: ClientIp,
+ nt: Notify<'_>,
) -> Json<Value> {
let data = data.into_inner().data;
@@ -966,7 +967,8 @@ 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).await {
+ let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &ip, &nt).await
+ {
Ok(_) => String::new(),
Err(e) => format!("{:?}", e),
};
@@ -998,10 +1000,11 @@ async fn confirm_invite(
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).await
+ _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &ip, &nt).await
}
async fn _confirm_invite(
@@ -1011,6 +1014,7 @@ async fn _confirm_invite(
headers: &AdminHeaders,
conn: &mut DbConn,
ip: &ClientIp,
+ nt: &Notify<'_>,
) -> EmptyResult {
if key.is_empty() || org_user_id.is_empty() {
err!("Key or UserId is not set, unable to process request");
@@ -1069,7 +1073,13 @@ async fn _confirm_invite(
mail::send_invite_confirmed(&address, &org_name).await?;
}
- user_to_confirm.save(conn).await
+ let save_result = user_to_confirm.save(conn).await;
+
+ if let Some(user) = User::find_by_uuid(&user_to_confirm.user_uuid, conn).await {
+ nt.send_user_update(UpdateType::SyncOrgKeys, &user).await;
+ }
+
+ save_result
}
#[get("/organizations/<org_id>/users/<org_user_id>")]
@@ -1206,12 +1216,13 @@ async fn bulk_delete_user(
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).await {
+ let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await {
Ok(_) => String::new(),
Err(e) => format!("{:?}", e),
};
@@ -1239,8 +1250,9 @@ async fn delete_user(
headers: AdminHeaders,
mut conn: DbConn,
ip: ClientIp,
+ nt: Notify<'_>,
) -> EmptyResult {
- _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await
+ _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await
}
#[post("/organizations/<org_id>/users/<org_user_id>/delete")]
@@ -1250,8 +1262,9 @@ async fn post_delete_user(
headers: AdminHeaders,
mut conn: DbConn,
ip: ClientIp,
+ nt: Notify<'_>,
) -> EmptyResult {
- _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await
+ _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await
}
async fn _delete_user(
@@ -1260,6 +1273,7 @@ async fn _delete_user(
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 {
Some(user) => user,
@@ -1288,6 +1302,10 @@ async fn _delete_user(
)
.await;
+ if let Some(user) = User::find_by_uuid(&user_to_delete.user_uuid, conn).await {
+ nt.send_user_update(UpdateType::SyncOrgKeys, &user).await;
+ }
+
user_to_delete.delete(conn).await
}
diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs
@@ -355,6 +355,7 @@ async fn post_access(
data: JsonUpcase<SendAccessData>,
mut conn: DbConn,
ip: ClientIp,
+ nt: Notify<'_>,
) -> JsonResult {
let mut send = match Send::find_by_access_id(&access_id, &mut conn).await {
Some(s) => s,
@@ -396,6 +397,8 @@ async fn post_access(
send.save(&mut conn).await?;
+ nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await).await;
+
Ok(Json(send.to_json_access(&mut conn).await))
}
@@ -406,6 +409,7 @@ async fn post_access_file(
data: JsonUpcase<SendAccessData>,
host: Host,
mut conn: DbConn,
+ nt: Notify<'_>,
) -> JsonResult {
let mut send = match Send::find_by_uuid(&send_id, &mut conn).await {
Some(s) => s,
@@ -444,6 +448,8 @@ async fn post_access_file(
send.save(&mut conn).await?;
+ nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await).await;
+
let token_claims = crate::auth::generate_send_claims(&send_id, &file_id);
let token = crate::auth::encode_jwt(&token_claims);
Ok(Json(json!({
diff --git a/src/api/notifications.rs b/src/api/notifications.rs
@@ -164,12 +164,13 @@ impl WebSocketUsers {
let data = create_update(
vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))],
ut,
+ None,
);
self.send_update(&user.uuid, &data).await;
}
- pub async fn send_folder_update(&self, ut: UpdateType, folder: &Folder) {
+ pub async fn send_folder_update(&self, ut: UpdateType, folder: &Folder, acting_device_uuid: &String) {
let data = create_update(
vec![
("Id".into(), folder.uuid.clone().into()),
@@ -177,12 +178,19 @@ impl WebSocketUsers {
("RevisionDate".into(), serialize_date(folder.updated_at)),
],
ut,
+ Some(acting_device_uuid.into()),
);
self.send_update(&folder.user_uuid, &data).await;
}
- pub async fn send_cipher_update(&self, ut: UpdateType, cipher: &Cipher, user_uuids: &[String]) {
+ pub async fn send_cipher_update(
+ &self,
+ ut: UpdateType,
+ cipher: &Cipher,
+ user_uuids: &[String],
+ acting_device_uuid: &String,
+ ) {
let user_uuid = convert_option(cipher.user_uuid.clone());
let org_uuid = convert_option(cipher.organization_uuid.clone());
@@ -195,6 +203,7 @@ impl WebSocketUsers {
("RevisionDate".into(), serialize_date(cipher.updated_at)),
],
ut,
+ Some(acting_device_uuid.into()),
);
for uuid in user_uuids {
@@ -212,6 +221,7 @@ impl WebSocketUsers {
("RevisionDate".into(), serialize_date(send.revision_date)),
],
ut,
+ None,
);
for uuid in user_uuids {
@@ -228,14 +238,14 @@ impl WebSocketUsers {
"ReceiveMessage", // Target
[ // Arguments
{
- "ContextId": "app_id",
+ "ContextId": acting_device_uuid || Nil,
"Type": ut as i32,
"Payload": {}
}
]
]
*/
-fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType) -> Vec<u8> {
+fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_uuid: Option<String>) -> Vec<u8> {
use rmpv::Value as V;
let value = V::Array(vec![
@@ -244,7 +254,7 @@ fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType) -> Vec<u8> {
V::Nil,
"ReceiveMessage".into(),
V::Array(vec![V::Map(vec![
- ("ContextId".into(), "app_id".into()),
+ ("ContextId".into(), acting_device_uuid.map(|v| v.into()).unwrap_or_else(|| V::Nil)),
("Type".into(), (ut as i32).into()),
("Payload".into(), payload.into()),
])]),
@@ -260,17 +270,17 @@ fn create_ping() -> Vec<u8> {
#[allow(dead_code)]
#[derive(Eq, PartialEq)]
pub enum UpdateType {
- CipherUpdate = 0,
- CipherCreate = 1,
- LoginDelete = 2,
- FolderDelete = 3,
- Ciphers = 4,
-
- Vault = 5,
- OrgKeys = 6,
- FolderCreate = 7,
- FolderUpdate = 8,
- CipherDelete = 9,
+ SyncCipherUpdate = 0,
+ SyncCipherCreate = 1,
+ SyncLoginDelete = 2,
+ SyncFolderDelete = 3,
+ SyncCiphers = 4,
+
+ SyncVault = 5,
+ SyncOrgKeys = 6,
+ SyncFolderCreate = 7,
+ SyncFolderUpdate = 8,
+ SyncCipherDelete = 9,
SyncSettings = 10,
LogOut = 11,
@@ -279,6 +289,9 @@ pub enum UpdateType {
SyncSendUpdate = 13,
SyncSendDelete = 14,
+ AuthRequest = 15,
+ AuthRequestResponse = 16,
+
None = 100,
}