Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2024-11-26 05:50:29 +01:00
Merge branch 'BlackDex-update-notifications'
Dieser Commit ist enthalten in:
Commit
05a552910c
8 geänderte Dateien mit 187 neuen und 63 gelöschten Zeilen
|
@ -13,7 +13,7 @@ use rocket::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
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},
|
auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp},
|
||||||
config::ConfigBuilder,
|
config::ConfigBuilder,
|
||||||
db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
|
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")]
|
#[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?;
|
let mut user = get_user_or_404(&uuid, &mut conn).await?;
|
||||||
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
|
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
|
||||||
user.reset_security_stamp();
|
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")]
|
#[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?;
|
let mut user = get_user_or_404(&uuid, &mut conn).await?;
|
||||||
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
|
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
|
||||||
user.reset_security_stamp();
|
user.reset_security_stamp();
|
||||||
user.enabled = false;
|
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")]
|
#[post("/users/<uuid>/enable")]
|
||||||
|
|
|
@ -275,6 +275,7 @@ async fn post_password(
|
||||||
headers: Headers,
|
headers: Headers,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
ip: ClientIp,
|
ip: ClientIp,
|
||||||
|
nt: Notify<'_>,
|
||||||
) -> EmptyResult {
|
) -> EmptyResult {
|
||||||
let data: ChangePassData = data.into_inner().data;
|
let data: ChangePassData = data.into_inner().data;
|
||||||
let mut user = headers.user;
|
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")]),
|
Some(vec![String::from("post_rotatekey"), String::from("get_contacts"), String::from("get_public_keys")]),
|
||||||
);
|
);
|
||||||
user.akey = data.Key;
|
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)]
|
#[derive(Deserialize)]
|
||||||
|
@ -308,7 +313,7 @@ struct ChangeKdfData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/accounts/kdf", data = "<data>")]
|
#[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 data: ChangeKdfData = data.into_inner().data;
|
||||||
let mut user = headers.user;
|
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.client_kdf_type = data.Kdf;
|
||||||
user.set_password(&data.NewMasterPasswordHash, None);
|
user.set_password(&data.NewMasterPasswordHash, None);
|
||||||
user.akey = data.Key;
|
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)]
|
#[derive(Deserialize)]
|
||||||
|
@ -388,6 +397,7 @@ async fn post_rotatekey(
|
||||||
|
|
||||||
// Prevent triggering cipher updates via WebSockets by settings UpdateType::None
|
// 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.
|
// 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)
|
update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
|
@ -399,11 +409,20 @@ async fn post_rotatekey(
|
||||||
user.private_key = Some(data.PrivateKey);
|
user.private_key = Some(data.PrivateKey);
|
||||||
user.reset_security_stamp();
|
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>")]
|
#[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 data: PasswordData = data.into_inner().data;
|
||||||
let mut user = headers.user;
|
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?;
|
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
|
||||||
user.reset_security_stamp();
|
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)]
|
#[derive(Deserialize)]
|
||||||
|
@ -465,7 +488,12 @@ struct ChangeEmailData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/accounts/email", data = "<data>")]
|
#[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 data: ChangeEmailData = data.into_inner().data;
|
||||||
let mut user = headers.user;
|
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.set_password(&data.NewMasterPasswordHash, None);
|
||||||
user.akey = data.Key;
|
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")]
|
#[post("/accounts/verify-email")]
|
||||||
|
|
|
@ -310,7 +310,8 @@ async fn post_ciphers(
|
||||||
data.LastKnownRevisionDate = None;
|
data.LastKnownRevisionDate = None;
|
||||||
|
|
||||||
let mut cipher = Cipher::new(data.Type, data.Name.clone());
|
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))
|
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 {
|
for (id, attachment) in attachments {
|
||||||
let mut saved_att = match Attachment::find_by_id(&id, conn).await {
|
let mut saved_att = match Attachment::find_by_id(&id, conn).await {
|
||||||
Some(att) => att,
|
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 {
|
if saved_att.cipher_uuid != cipher.uuid {
|
||||||
|
@ -482,8 +490,8 @@ pub async fn update_cipher_from_data(
|
||||||
// Only log events for organizational ciphers
|
// Only log events for organizational ciphers
|
||||||
if let Some(org_uuid) = &cipher.organization_uuid {
|
if let Some(org_uuid) = &cipher.organization_uuid {
|
||||||
let event_type = match (&ut, transfer_cipher) {
|
let event_type = match (&ut, transfer_cipher) {
|
||||||
(UpdateType::CipherCreate, true) => EventType::CipherCreated,
|
(UpdateType::SyncCipherCreate, true) => EventType::CipherCreated,
|
||||||
(UpdateType::CipherUpdate, true) => EventType::CipherShared,
|
(UpdateType::SyncCipherUpdate, true) => EventType::CipherShared,
|
||||||
(_, _) => EventType::CipherUpdated,
|
(_, _) => EventType::CipherUpdated,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -499,7 +507,7 @@ pub async fn update_cipher_from_data(
|
||||||
.await;
|
.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(())
|
Ok(())
|
||||||
|
@ -562,7 +570,7 @@ async fn post_ciphers_import(
|
||||||
|
|
||||||
let mut user = headers.user;
|
let mut user = headers.user;
|
||||||
user.update_revision(&mut conn).await?;
|
user.update_revision(&mut conn).await?;
|
||||||
nt.send_user_update(UpdateType::Vault, &user).await;
|
nt.send_user_update(UpdateType::SyncVault, &user).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,7 +636,8 @@ async fn put_cipher(
|
||||||
err!("Cipher is not write accessible")
|
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))
|
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.
|
// When LastKnownRevisionDate is None, it is a new cipher, so send CipherCreate.
|
||||||
let ut = if data.Cipher.LastKnownRevisionDate.is_some() {
|
let ut = if data.Cipher.LastKnownRevisionDate.is_some() {
|
||||||
UpdateType::CipherUpdate
|
UpdateType::SyncCipherUpdate
|
||||||
} else {
|
} else {
|
||||||
UpdateType::CipherCreate
|
UpdateType::SyncCipherCreate
|
||||||
};
|
};
|
||||||
|
|
||||||
update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, ip, nt, ut).await?;
|
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?
|
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 {
|
if let Some(org_uuid) = &cipher.organization_uuid {
|
||||||
log_event(
|
log_event(
|
||||||
|
@ -1390,7 +1405,7 @@ async fn move_cipher_selected(
|
||||||
// Move cipher
|
// Move cipher
|
||||||
cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &mut conn).await?;
|
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(())
|
Ok(())
|
||||||
|
@ -1438,7 +1453,7 @@ async fn delete_all(
|
||||||
Some(user_org) => {
|
Some(user_org) => {
|
||||||
if user_org.atype == UserOrgType::Owner {
|
if user_org.atype == UserOrgType::Owner {
|
||||||
Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?;
|
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(
|
log_event(
|
||||||
EventType::OrganizationPurgedVault as i32,
|
EventType::OrganizationPurgedVault as i32,
|
||||||
|
@ -1471,7 +1486,7 @@ async fn delete_all(
|
||||||
}
|
}
|
||||||
|
|
||||||
user.update_revision(&mut conn).await?;
|
user.update_revision(&mut conn).await?;
|
||||||
nt.send_user_update(UpdateType::Vault, &user).await;
|
nt.send_user_update(UpdateType::SyncVault, &user).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1497,10 +1512,22 @@ async fn _delete_cipher_by_uuid(
|
||||||
if soft_delete {
|
if soft_delete {
|
||||||
cipher.deleted_at = Some(Utc::now().naive_utc());
|
cipher.deleted_at = Some(Utc::now().naive_utc());
|
||||||
cipher.save(conn).await?;
|
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 {
|
} else {
|
||||||
cipher.delete(conn).await?;
|
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 {
|
if let Some(org_uuid) = cipher.organization_uuid {
|
||||||
|
@ -1562,7 +1589,13 @@ async fn _restore_cipher_by_uuid(
|
||||||
cipher.deleted_at = None;
|
cipher.deleted_at = None;
|
||||||
cipher.save(conn).await?;
|
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 {
|
if let Some(org_uuid) = &cipher.organization_uuid {
|
||||||
log_event(
|
log_event(
|
||||||
EventType::CipherRestored as i32,
|
EventType::CipherRestored as i32,
|
||||||
|
@ -1639,7 +1672,13 @@ async fn _delete_cipher_attachment_by_id(
|
||||||
|
|
||||||
// Delete attachment
|
// Delete attachment
|
||||||
attachment.delete(conn).await?;
|
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 {
|
if let Some(org_uuid) = cipher.organization_uuid {
|
||||||
log_event(
|
log_event(
|
||||||
EventType::CipherAttachmentDeleted as i32,
|
EventType::CipherAttachmentDeleted as i32,
|
||||||
|
|
|
@ -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);
|
let mut folder = Folder::new(headers.user.uuid, data.Name);
|
||||||
|
|
||||||
folder.save(&mut conn).await?;
|
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()))
|
Ok(Json(folder.to_json()))
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ async fn put_folder(
|
||||||
folder.name = data.Name;
|
folder.name = data.Name;
|
||||||
|
|
||||||
folder.save(&mut conn).await?;
|
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()))
|
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
|
// Delete the actual folder entry
|
||||||
folder.delete(&mut conn).await?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,7 @@ mod organizations;
|
||||||
mod sends;
|
mod sends;
|
||||||
pub mod two_factor;
|
pub mod two_factor;
|
||||||
|
|
||||||
pub use ciphers::purge_trashed_ciphers;
|
pub use ciphers::{purge_trashed_ciphers, CipherSyncData, CipherSyncType};
|
||||||
pub use ciphers::{CipherSyncData, CipherSyncType};
|
|
||||||
pub use emergency_access::{emergency_notification_reminder_job, emergency_request_timeout_job};
|
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 events::{event_cleanup_job, log_event, log_user_event};
|
||||||
pub use sends::purge_sends;
|
pub use sends::purge_sends;
|
||||||
|
@ -47,13 +46,11 @@ pub fn events_routes() -> Vec<Route> {
|
||||||
//
|
//
|
||||||
// Move this somewhere else
|
// Move this somewhere else
|
||||||
//
|
//
|
||||||
use rocket::serde::json::Json;
|
use rocket::{serde::json::Json, Catcher, Route};
|
||||||
use rocket::Catcher;
|
|
||||||
use rocket::Route;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{JsonResult, JsonUpcase},
|
api::{JsonResult, JsonUpcase, Notify, UpdateType},
|
||||||
auth::Headers,
|
auth::Headers,
|
||||||
db::DbConn,
|
db::DbConn,
|
||||||
error::Error,
|
error::Error,
|
||||||
|
@ -138,7 +135,12 @@ struct EquivDomainData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/settings/domains", data = "<data>")]
|
#[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 data: EquivDomainData = data.into_inner().data;
|
||||||
|
|
||||||
let excluded_globals = data.ExcludedGlobalEquivalentDomains.unwrap_or_default();
|
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?;
|
user.save(&mut conn).await?;
|
||||||
|
|
||||||
|
nt.send_user_update(UpdateType::SyncSettings, &user).await;
|
||||||
|
|
||||||
Ok(Json(json!({})))
|
Ok(Json(json!({})))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/settings/domains", data = "<data>")]
|
#[put("/settings/domains", data = "<data>")]
|
||||||
async fn put_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbConn) -> JsonResult {
|
async fn put_eq_domains(
|
||||||
post_eq_domains(data, headers, conn).await
|
data: JsonUpcase<EquivDomainData>,
|
||||||
|
headers: Headers,
|
||||||
|
conn: DbConn,
|
||||||
|
nt: Notify<'_>,
|
||||||
|
) -> JsonResult {
|
||||||
|
post_eq_domains(data, headers, conn, nt).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/hibp/breach?<username>")]
|
#[get("/hibp/breach?<username>")]
|
||||||
|
|
|
@ -957,6 +957,7 @@ async fn bulk_confirm_invite(
|
||||||
headers: AdminHeaders,
|
headers: AdminHeaders,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
ip: ClientIp,
|
ip: ClientIp,
|
||||||
|
nt: Notify<'_>,
|
||||||
) -> Json<Value> {
|
) -> Json<Value> {
|
||||||
let data = data.into_inner().data;
|
let data = data.into_inner().data;
|
||||||
|
|
||||||
|
@ -966,7 +967,8 @@ async fn bulk_confirm_invite(
|
||||||
for invite in keys {
|
for invite in keys {
|
||||||
let org_user_id = invite["Id"].as_str().unwrap_or_default();
|
let org_user_id = invite["Id"].as_str().unwrap_or_default();
|
||||||
let user_key = invite["Key"].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(),
|
Ok(_) => String::new(),
|
||||||
Err(e) => format!("{:?}", e),
|
Err(e) => format!("{:?}", e),
|
||||||
};
|
};
|
||||||
|
@ -998,10 +1000,11 @@ async fn confirm_invite(
|
||||||
headers: AdminHeaders,
|
headers: AdminHeaders,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
ip: ClientIp,
|
ip: ClientIp,
|
||||||
|
nt: Notify<'_>,
|
||||||
) -> EmptyResult {
|
) -> EmptyResult {
|
||||||
let data = data.into_inner().data;
|
let data = data.into_inner().data;
|
||||||
let user_key = data["Key"].as_str().unwrap_or_default();
|
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(
|
async fn _confirm_invite(
|
||||||
|
@ -1011,6 +1014,7 @@ async fn _confirm_invite(
|
||||||
headers: &AdminHeaders,
|
headers: &AdminHeaders,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
ip: &ClientIp,
|
ip: &ClientIp,
|
||||||
|
nt: &Notify<'_>,
|
||||||
) -> EmptyResult {
|
) -> EmptyResult {
|
||||||
if key.is_empty() || org_user_id.is_empty() {
|
if key.is_empty() || org_user_id.is_empty() {
|
||||||
err!("Key or UserId is not set, unable to process request");
|
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?;
|
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>")]
|
#[get("/organizations/<org_id>/users/<org_user_id>")]
|
||||||
|
@ -1206,12 +1216,13 @@ async fn bulk_delete_user(
|
||||||
headers: AdminHeaders,
|
headers: AdminHeaders,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
ip: ClientIp,
|
ip: ClientIp,
|
||||||
|
nt: Notify<'_>,
|
||||||
) -> Json<Value> {
|
) -> Json<Value> {
|
||||||
let data: OrgBulkIds = data.into_inner().data;
|
let data: OrgBulkIds = data.into_inner().data;
|
||||||
|
|
||||||
let mut bulk_response = Vec::new();
|
let mut bulk_response = Vec::new();
|
||||||
for org_user_id in data.Ids {
|
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(),
|
Ok(_) => String::new(),
|
||||||
Err(e) => format!("{:?}", e),
|
Err(e) => format!("{:?}", e),
|
||||||
};
|
};
|
||||||
|
@ -1239,8 +1250,9 @@ async fn delete_user(
|
||||||
headers: AdminHeaders,
|
headers: AdminHeaders,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
ip: ClientIp,
|
ip: ClientIp,
|
||||||
|
nt: Notify<'_>,
|
||||||
) -> EmptyResult {
|
) -> 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")]
|
#[post("/organizations/<org_id>/users/<org_user_id>/delete")]
|
||||||
|
@ -1250,8 +1262,9 @@ async fn post_delete_user(
|
||||||
headers: AdminHeaders,
|
headers: AdminHeaders,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
ip: ClientIp,
|
ip: ClientIp,
|
||||||
|
nt: Notify<'_>,
|
||||||
) -> EmptyResult {
|
) -> 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(
|
async fn _delete_user(
|
||||||
|
@ -1260,6 +1273,7 @@ async fn _delete_user(
|
||||||
headers: &AdminHeaders,
|
headers: &AdminHeaders,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
ip: &ClientIp,
|
ip: &ClientIp,
|
||||||
|
nt: &Notify<'_>,
|
||||||
) -> EmptyResult {
|
) -> EmptyResult {
|
||||||
let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await {
|
let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await {
|
||||||
Some(user) => user,
|
Some(user) => user,
|
||||||
|
@ -1288,6 +1302,10 @@ async fn _delete_user(
|
||||||
)
|
)
|
||||||
.await;
|
.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
|
user_to_delete.delete(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -355,6 +355,7 @@ async fn post_access(
|
||||||
data: JsonUpcase<SendAccessData>,
|
data: JsonUpcase<SendAccessData>,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
ip: ClientIp,
|
ip: ClientIp,
|
||||||
|
nt: Notify<'_>,
|
||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
let mut send = match Send::find_by_access_id(&access_id, &mut conn).await {
|
let mut send = match Send::find_by_access_id(&access_id, &mut conn).await {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
|
@ -396,6 +397,8 @@ 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).await;
|
||||||
|
|
||||||
Ok(Json(send.to_json_access(&mut conn).await))
|
Ok(Json(send.to_json_access(&mut conn).await))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,6 +409,7 @@ async fn post_access_file(
|
||||||
data: JsonUpcase<SendAccessData>,
|
data: JsonUpcase<SendAccessData>,
|
||||||
host: Host,
|
host: Host,
|
||||||
mut conn: DbConn,
|
mut conn: DbConn,
|
||||||
|
nt: Notify<'_>,
|
||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
let mut send = match Send::find_by_uuid(&send_id, &mut conn).await {
|
let mut send = match Send::find_by_uuid(&send_id, &mut conn).await {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
|
@ -444,6 +448,8 @@ 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).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);
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
|
|
|
@ -164,12 +164,13 @@ impl WebSocketUsers {
|
||||||
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))],
|
||||||
ut,
|
ut,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.send_update(&user.uuid, &data).await;
|
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(
|
let data = create_update(
|
||||||
vec![
|
vec![
|
||||||
("Id".into(), folder.uuid.clone().into()),
|
("Id".into(), folder.uuid.clone().into()),
|
||||||
|
@ -177,12 +178,19 @@ impl WebSocketUsers {
|
||||||
("RevisionDate".into(), serialize_date(folder.updated_at)),
|
("RevisionDate".into(), serialize_date(folder.updated_at)),
|
||||||
],
|
],
|
||||||
ut,
|
ut,
|
||||||
|
Some(acting_device_uuid.into()),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.send_update(&folder.user_uuid, &data).await;
|
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 user_uuid = convert_option(cipher.user_uuid.clone());
|
||||||
let org_uuid = convert_option(cipher.organization_uuid.clone());
|
let org_uuid = convert_option(cipher.organization_uuid.clone());
|
||||||
|
|
||||||
|
@ -195,6 +203,7 @@ impl WebSocketUsers {
|
||||||
("RevisionDate".into(), serialize_date(cipher.updated_at)),
|
("RevisionDate".into(), serialize_date(cipher.updated_at)),
|
||||||
],
|
],
|
||||||
ut,
|
ut,
|
||||||
|
Some(acting_device_uuid.into()),
|
||||||
);
|
);
|
||||||
|
|
||||||
for uuid in user_uuids {
|
for uuid in user_uuids {
|
||||||
|
@ -212,6 +221,7 @@ impl WebSocketUsers {
|
||||||
("RevisionDate".into(), serialize_date(send.revision_date)),
|
("RevisionDate".into(), serialize_date(send.revision_date)),
|
||||||
],
|
],
|
||||||
ut,
|
ut,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
for uuid in user_uuids {
|
for uuid in user_uuids {
|
||||||
|
@ -228,14 +238,14 @@ impl WebSocketUsers {
|
||||||
"ReceiveMessage", // Target
|
"ReceiveMessage", // Target
|
||||||
[ // Arguments
|
[ // Arguments
|
||||||
{
|
{
|
||||||
"ContextId": "app_id",
|
"ContextId": acting_device_uuid || Nil,
|
||||||
"Type": ut as i32,
|
"Type": ut as i32,
|
||||||
"Payload": {}
|
"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;
|
use rmpv::Value as V;
|
||||||
|
|
||||||
let value = V::Array(vec![
|
let value = V::Array(vec![
|
||||||
|
@ -244,7 +254,7 @@ fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType) -> Vec<u8> {
|
||||||
V::Nil,
|
V::Nil,
|
||||||
"ReceiveMessage".into(),
|
"ReceiveMessage".into(),
|
||||||
V::Array(vec![V::Map(vec![
|
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()),
|
("Type".into(), (ut as i32).into()),
|
||||||
("Payload".into(), payload.into()),
|
("Payload".into(), payload.into()),
|
||||||
])]),
|
])]),
|
||||||
|
@ -260,17 +270,17 @@ fn create_ping() -> Vec<u8> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Eq, PartialEq)]
|
#[derive(Eq, PartialEq)]
|
||||||
pub enum UpdateType {
|
pub enum UpdateType {
|
||||||
CipherUpdate = 0,
|
SyncCipherUpdate = 0,
|
||||||
CipherCreate = 1,
|
SyncCipherCreate = 1,
|
||||||
LoginDelete = 2,
|
SyncLoginDelete = 2,
|
||||||
FolderDelete = 3,
|
SyncFolderDelete = 3,
|
||||||
Ciphers = 4,
|
SyncCiphers = 4,
|
||||||
|
|
||||||
Vault = 5,
|
SyncVault = 5,
|
||||||
OrgKeys = 6,
|
SyncOrgKeys = 6,
|
||||||
FolderCreate = 7,
|
SyncFolderCreate = 7,
|
||||||
FolderUpdate = 8,
|
SyncFolderUpdate = 8,
|
||||||
CipherDelete = 9,
|
SyncCipherDelete = 9,
|
||||||
SyncSettings = 10,
|
SyncSettings = 10,
|
||||||
|
|
||||||
LogOut = 11,
|
LogOut = 11,
|
||||||
|
@ -279,6 +289,9 @@ pub enum UpdateType {
|
||||||
SyncSendUpdate = 13,
|
SyncSendUpdate = 13,
|
||||||
SyncSendDelete = 14,
|
SyncSendDelete = 14,
|
||||||
|
|
||||||
|
AuthRequest = 15,
|
||||||
|
AuthRequestResponse = 16,
|
||||||
|
|
||||||
None = 100,
|
None = 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Laden …
In neuem Issue referenzieren