Fix user deletion to update more tables (#5351)
* wip: fix user delete * add wrap_errs * delete more rows in user deletion * sqlx prepare
This commit is contained in:
@@ -7,6 +7,7 @@ use crate::database::redis::RedisPool;
|
||||
use crate::database::{PgTransaction, models};
|
||||
use crate::models::billing::ChargeStatus;
|
||||
use crate::models::users::Badges;
|
||||
use crate::util::error::Context;
|
||||
use ariadne::ids::base62_impl::{parse_base62, to_base62};
|
||||
use chrono::{DateTime, Utc};
|
||||
use dashmap::DashMap;
|
||||
@@ -504,12 +505,15 @@ impl DBUser {
|
||||
id: DBUserId,
|
||||
transaction: &mut PgTransaction<'_>,
|
||||
redis: &RedisPool,
|
||||
) -> Result<Option<()>, DatabaseError> {
|
||||
let user = Self::get_id(id, &mut *transaction, redis).await?;
|
||||
) -> Result<Option<()>, eyre::Report> {
|
||||
let user = Self::get_id(id, &mut *transaction, redis)
|
||||
.await
|
||||
.wrap_err("failed to get user by ID")?;
|
||||
|
||||
if let Some(delete_user) = user {
|
||||
DBUser::clear_caches(&[(id, Some(delete_user.username))], redis)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to clear caches")?;
|
||||
|
||||
let deleted_user: DBUserId =
|
||||
crate::models::users::DELETED_USER.into();
|
||||
@@ -524,7 +528,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update team_members owner")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -536,7 +541,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update versions author_id")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -548,7 +554,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update shared_instances owner_id")?;
|
||||
|
||||
use futures::TryStreamExt;
|
||||
let notifications: Vec<i64> = sqlx::query!(
|
||||
@@ -561,7 +568,8 @@ impl DBUser {
|
||||
.fetch(&mut *transaction)
|
||||
.map_ok(|m| m.id)
|
||||
.try_collect::<Vec<i64>>()
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to fetch notifications")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -571,7 +579,8 @@ impl DBUser {
|
||||
¬ifications
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete notifications_actions")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -581,7 +590,8 @@ impl DBUser {
|
||||
id as DBUserId
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete notifications_deliveries")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -591,7 +601,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete notifications")?;
|
||||
|
||||
let user_collections = sqlx::query!(
|
||||
"
|
||||
@@ -604,11 +615,13 @@ impl DBUser {
|
||||
.fetch(&mut *transaction)
|
||||
.map_ok(|x| DBCollectionId(x.id))
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to fetch user collections")?;
|
||||
|
||||
for collection_id in user_collections {
|
||||
models::DBCollection::remove(collection_id, transaction, redis)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to remove collection")?;
|
||||
}
|
||||
|
||||
let report_threads = sqlx::query!(
|
||||
@@ -623,10 +636,13 @@ impl DBUser {
|
||||
.fetch(&mut *transaction)
|
||||
.map_ok(|x| DBThreadId(x.id))
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to fetch report threads")?;
|
||||
|
||||
for thread_id in report_threads {
|
||||
models::DBThread::remove_full(thread_id, transaction).await?;
|
||||
models::DBThread::remove_full(thread_id, transaction)
|
||||
.await
|
||||
.wrap_err("failed to remove thread")?;
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
@@ -637,7 +653,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete reports")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -649,7 +666,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update reports user_id")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -659,7 +677,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete mod_follows")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -669,29 +688,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM payouts_values
|
||||
WHERE user_id = $1
|
||||
",
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE payouts
|
||||
SET user_id = $1
|
||||
WHERE user_id = $2
|
||||
",
|
||||
deleted_user as DBUserId,
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete team_members")?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -703,7 +701,8 @@ impl DBUser {
|
||||
deleted_user as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update threads_messages")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -713,7 +712,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete threads_members")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -725,7 +725,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update uploaded_images owner_id")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -735,7 +736,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete sessions")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -745,7 +747,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete pats")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -755,7 +758,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete friends")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -766,7 +770,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update affiliate_codes created_by")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -775,7 +780,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete affiliate_codes")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -786,7 +792,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update payouts_values user_id")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -795,7 +802,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete payouts_values_notifications")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -807,21 +815,28 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update charges user_id")?;
|
||||
|
||||
let open_subscriptions =
|
||||
DBUserSubscription::get_all_user(id, &mut *transaction).await?;
|
||||
DBUserSubscription::get_all_user(id, &mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to get user subscriptions")?;
|
||||
|
||||
for x in open_subscriptions {
|
||||
let charge =
|
||||
DBCharge::get_open_subscription(x.id, &mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to get open subscription charge")?;
|
||||
if let Some(mut charge) = charge {
|
||||
charge.status = ChargeStatus::Cancelled;
|
||||
charge.due = Utc::now();
|
||||
charge.user_id = deleted_user;
|
||||
|
||||
charge.upsert(transaction).await?;
|
||||
charge
|
||||
.upsert(transaction)
|
||||
.await
|
||||
.wrap_err("failed to upsert charge")?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,7 +850,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to update users_subscriptions user_id")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -845,7 +861,8 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete user_backup_codes")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -855,7 +872,113 @@ impl DBUser {
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
.await
|
||||
.wrap_err("failed to delete user")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM oauth_client_authorizations
|
||||
WHERE user_id = $1
|
||||
",
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to delete oauth_client_authorizations")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM shared_instance_users
|
||||
WHERE user_id = $1
|
||||
",
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to delete shared_instance_users")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM shared_instance_invited_users
|
||||
WHERE invited_user_id = $1
|
||||
",
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to delete shared_instance_invited_users")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users_redeemals
|
||||
SET user_id = $1
|
||||
WHERE user_id = $2
|
||||
",
|
||||
deleted_user as DBUserId,
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to delete users_redeemals")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users_compliance
|
||||
SET user_id = $1
|
||||
WHERE user_id = $2
|
||||
",
|
||||
deleted_user as DBUserId,
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to delete users_compliance")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM user_limits
|
||||
WHERE user_id = $1
|
||||
",
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to delete user_limits")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM users_notifications_preferences
|
||||
WHERE user_id = $1
|
||||
",
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to delete users_notifications_preferences")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM moderation_locks
|
||||
WHERE moderator_id = $1
|
||||
",
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to delete moderation_locks")?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE oauth_clients
|
||||
SET created_by = $1
|
||||
WHERE created_by = $2
|
||||
",
|
||||
deleted_user as DBUserId,
|
||||
id as DBUserId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.wrap_err("failed to update oauth_clients created_by")?;
|
||||
|
||||
Ok(Some(()))
|
||||
} else {
|
||||
|
||||
@@ -251,6 +251,7 @@ pub async fn user_delete(
|
||||
// Returns NoContent, so we don't need to convert to V2
|
||||
v3::users::user_delete(req, info, pool, redis, session_queue)
|
||||
.await
|
||||
.map(|()| HttpResponse::NoContent().body(""))
|
||||
.or_else(v2_reroute::flatten_404_error)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use super::{ApiError, oauth_clients::get_user_clients};
|
||||
use crate::database::PgPool;
|
||||
use crate::util::error::Context;
|
||||
use crate::{
|
||||
auth::{
|
||||
checks::is_visible_organization, filter_visible_collections,
|
||||
@@ -680,7 +681,7 @@ pub async fn user_delete(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<RedisPool>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
) -> Result<(), ApiError> {
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
@@ -690,26 +691,33 @@ pub async fn user_delete(
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id_option = DBUser::get(&info.into_inner().0, &**pool, &redis).await?;
|
||||
let id_option = DBUser::get(&info.into_inner().0, &**pool, &redis)
|
||||
.await
|
||||
.wrap_internal_err("failed to get user")?;
|
||||
|
||||
if let Some(id) = id_option.map(|x| x.id) {
|
||||
if !user.role.is_admin() && user.id != id.into() {
|
||||
return Err(ApiError::CustomAuthentication(
|
||||
"You do not have permission to delete this user!".to_string(),
|
||||
));
|
||||
}
|
||||
let id = id_option.map(|x| x.id).ok_or(ApiError::NotFound)?;
|
||||
if !user.role.is_admin() && user.id != id.into() {
|
||||
return Err(ApiError::CustomAuthentication(
|
||||
"You do not have permission to delete this user!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
let mut transaction = pool
|
||||
.begin()
|
||||
.await
|
||||
.wrap_internal_err("failed to begin transaction")?;
|
||||
|
||||
let result = DBUser::remove(id, &mut transaction, &redis).await?;
|
||||
let result = DBUser::remove(id, &mut transaction, &redis)
|
||||
.await
|
||||
.wrap_internal_err("failed to remove user")?;
|
||||
|
||||
transaction.commit().await?;
|
||||
transaction
|
||||
.commit()
|
||||
.await
|
||||
.wrap_internal_err("failed to commit transaction")?;
|
||||
|
||||
if result.is_some() {
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
} else {
|
||||
Err(ApiError::NotFound)
|
||||
}
|
||||
if result.is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ApiError::NotFound)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user