mirror of
https://github.com/dani-garcia/vaultwarden.wiki.git
synced 2026-10-30 05:46:47 +02:00
Send logout notification when deleting a user (#7839)
This commit is contained in:
1 parent
f1c7eccd4a
commit
e056359a42
5 files changed
+63
-27
No files matched your search
+11
-3
@@ -17,7 +17,7 @@ use crate::{
|
||||
api::{
|
||||
ApiResult, EmptyResult, JsonResult, Notify,
|
||||
core::{log_event, two_factor},
|
||||
unregister_push_device,
|
||||
push_logout_to_relay, unregister_push_device,
|
||||
},
|
||||
auth::{ClientIp, Secure, decode_admin, encode_jwt, generate_admin_claims},
|
||||
config::ConfigBuilder,
|
||||
@@ -419,13 +419,21 @@ async fn get_user_json(user_id: UserId, _token: AdminToken, conn: DbConn) -> Jso
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/delete", format = "application/json")]
|
||||
async fn delete_user(user_id: UserId, token: AdminToken, conn: DbConn) -> EmptyResult {
|
||||
async fn delete_user(user_id: UserId, token: AdminToken, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
let user = get_user_or_404(&user_id, &conn).await?;
|
||||
|
||||
// Get the membership records before deleting the actual user
|
||||
// Get the membership records and push devices before deleting the actual user
|
||||
let memberships = Membership::find_any_state_by_user(&user_id, &conn).await;
|
||||
let has_push_device = CONFIG.push_enabled() && Device::check_user_has_push_device(&user_id, &conn).await;
|
||||
let res = user.delete(&conn).await;
|
||||
|
||||
if res.is_ok() {
|
||||
nt.send_logout(&user, None, &conn).await;
|
||||
if has_push_device {
|
||||
push_logout_to_relay(&user, None);
|
||||
}
|
||||
}
|
||||
|
||||
for membership in memberships {
|
||||
log_event(
|
||||
EventType::OrganizationUserDeleted,
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{
|
||||
api::{
|
||||
AnonymousNotify, ApiResult, EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType,
|
||||
core::{accept_org_invite, log_user_event, two_factor::email},
|
||||
master_password_policy, register_push_device, unregister_push_device,
|
||||
master_password_policy, push_logout_to_relay, register_push_device, unregister_push_device,
|
||||
},
|
||||
auth::{ClientHeaders, ClientIp, Headers, decode_delete, decode_invite, decode_verify_email},
|
||||
crypto,
|
||||
@@ -1301,7 +1301,7 @@ struct DeleteRecoverTokenData {
|
||||
}
|
||||
|
||||
#[post("/accounts/delete-recover-token", data = "<data>")]
|
||||
async fn post_delete_recover_token(data: Json<DeleteRecoverTokenData>, conn: DbConn) -> EmptyResult {
|
||||
async fn post_delete_recover_token(data: Json<DeleteRecoverTokenData>, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
let data: DeleteRecoverTokenData = data.into_inner();
|
||||
|
||||
let Ok(claims) = decode_delete(&data.token) else {
|
||||
@@ -1315,22 +1315,44 @@ async fn post_delete_recover_token(data: Json<DeleteRecoverTokenData>, conn: DbC
|
||||
if claims.sub != *user.uuid {
|
||||
err!("Invalid claim");
|
||||
}
|
||||
user.delete(&conn).await
|
||||
|
||||
// The devices are deleted together with the user, so check for push devices beforehand
|
||||
let has_push_device = CONFIG.push_enabled() && Device::check_user_has_push_device(&user.uuid, &conn).await;
|
||||
user.delete(&conn).await?;
|
||||
|
||||
nt.send_logout(&user, None, &conn).await;
|
||||
if has_push_device {
|
||||
push_logout_to_relay(&user, None);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[post("/accounts/delete", data = "<data>")]
|
||||
async fn post_delete_account(data: Json<PasswordOrOtpData>, headers: Headers, conn: DbConn) -> EmptyResult {
|
||||
delete_account(data, headers, conn).await
|
||||
async fn post_delete_account(
|
||||
data: Json<PasswordOrOtpData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
nt: Notify<'_>,
|
||||
) -> EmptyResult {
|
||||
delete_account(data, headers, conn, nt).await
|
||||
}
|
||||
|
||||
#[delete("/accounts", data = "<data>")]
|
||||
async fn delete_account(data: Json<PasswordOrOtpData>, headers: Headers, conn: DbConn) -> EmptyResult {
|
||||
async fn delete_account(data: Json<PasswordOrOtpData>, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
data.validate(&user, true, &conn).await?;
|
||||
|
||||
user.delete(&conn).await
|
||||
// The devices are deleted together with the user, so check for push devices beforehand
|
||||
let has_push_device = CONFIG.push_enabled() && Device::check_user_has_push_device(&user.uuid, &conn).await;
|
||||
user.delete(&conn).await?;
|
||||
|
||||
nt.send_logout(&user, None, &conn).await;
|
||||
if has_push_device {
|
||||
push_logout_to_relay(&user, None);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Not beneficial for Headers")]
|
||||
|
||||
+2
-2
@@ -25,8 +25,8 @@ pub use crate::api::{
|
||||
notifications::routes as notifications_routes,
|
||||
notifications::{AnonymousNotify, Notify, UpdateType, WS_ANONYMOUS_SUBSCRIPTIONS, WS_USERS},
|
||||
push::{
|
||||
push_cipher_update, push_folder_update, push_logout, push_send_update, push_user_update, register_push_device,
|
||||
unregister_push_device,
|
||||
push_cipher_update, push_folder_update, push_logout, push_logout_to_relay, push_send_update, push_user_update,
|
||||
register_push_device, unregister_push_device,
|
||||
},
|
||||
web::catchers as web_catchers,
|
||||
web::routes as web_routes,
|
||||
|
||||
+19
-13
@@ -194,22 +194,28 @@ pub async fn push_cipher_update(ut: UpdateType, cipher: &Cipher, device: &Device
|
||||
|
||||
pub async fn push_logout(user: &User, acting_device: Option<&Device>, conn: &DbConn) {
|
||||
if Device::check_user_has_push_device(&user.uuid, conn).await {
|
||||
tokio::task::spawn(send_to_push_relay(json!({
|
||||
"userId": user.uuid,
|
||||
"organizationId": (),
|
||||
"deviceId": acting_device.and_then(|d| d.push_uuid.as_ref()),
|
||||
"identifier": acting_device.map(|d| &d.uuid),
|
||||
"type": UpdateType::LogOut as i32,
|
||||
"payload": {
|
||||
"userId": user.uuid,
|
||||
"date": format_date(&user.updated_at)
|
||||
},
|
||||
"clientType": null,
|
||||
"installationId": null
|
||||
})));
|
||||
push_logout_to_relay(user, acting_device);
|
||||
}
|
||||
}
|
||||
|
||||
// Sends the logout without checking for push devices first.
|
||||
// Needed after deleting a user, when the devices are already gone from the database.
|
||||
pub fn push_logout_to_relay(user: &User, acting_device: Option<&Device>) {
|
||||
tokio::task::spawn(send_to_push_relay(json!({
|
||||
"userId": user.uuid,
|
||||
"organizationId": (),
|
||||
"deviceId": acting_device.and_then(|d| d.push_uuid.as_ref()),
|
||||
"identifier": acting_device.map(|d| &d.uuid),
|
||||
"type": UpdateType::LogOut as i32,
|
||||
"payload": {
|
||||
"userId": user.uuid,
|
||||
"date": format_date(&user.updated_at)
|
||||
},
|
||||
"clientType": null,
|
||||
"installationId": null
|
||||
})));
|
||||
}
|
||||
|
||||
pub async fn push_user_update(ut: UpdateType, user: &User, push_uuid: Option<&PushId>, conn: &DbConn) {
|
||||
if Device::check_user_has_push_device(&user.uuid, conn).await {
|
||||
tokio::task::spawn(send_to_push_relay(json!({
|
||||
|
||||
@@ -362,7 +362,7 @@ impl User {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete(self, conn: &DbConn) -> EmptyResult {
|
||||
pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
|
||||
for member in Membership::find_confirmed_by_user(&self.uuid, conn).await {
|
||||
if member.atype == MembershipType::Owner
|
||||
&& Membership::count_confirmed_by_org_and_type(&member.org_uuid, MembershipType::Owner, conn).await <= 1
|
||||
@@ -385,7 +385,7 @@ impl User {
|
||||
Invitation::take(&self.email, conn).await; // Delete invitation if any
|
||||
|
||||
conn.run(move |conn| {
|
||||
diesel::delete(users::table.filter(users::uuid.eq(self.uuid))).execute(conn).map_res("Error deleting user")
|
||||
diesel::delete(users::table.filter(users::uuid.eq(&self.uuid))).execute(conn).map_res("Error deleting user")
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user