Update WebSocket Notifications

Previously the websocket notifications were using `app_id` as the
`ContextId`. This was incorrect and should have been the device_uuid
from the client device executing the request. The clients will ignore
the websocket request if the uuid matches. This also fixes some issues
with the Desktop client which is able to modify attachments within the
same screen and causes an issue when saving the attachment afterwards.

Also changed the way to handle removed attachments, since that causes an
error saving the vault cipher afterwards, complaining about a missing
attachment. Bitwarden ignores this, and continues with the remaining
attachments (if any). This also fixes #2591 .

Further some more websocket notifications have been added to some other
functions which enhance the user experience.

- Logout users when deauthed, changed password, rotated keys
- Trigger OrgSyncKeys on user confirm and removal
- Added some extra to the send feature

Also renamed UpdateTypes to match Bitwarden naming.
This commit is contained in:
BlackDex
2022-12-30 21:23:55 +01:00
parent 10dadfca06
commit 996b60e43d
8 changed files with 187 additions and 63 deletions

View File

@ -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,
SyncCipherUpdate = 0,
SyncCipherCreate = 1,
SyncLoginDelete = 2,
SyncFolderDelete = 3,
SyncCiphers = 4,
Vault = 5,
OrgKeys = 6,
FolderCreate = 7,
FolderUpdate = 8,
CipherDelete = 9,
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,
}