From c986f5961c4a04d30d5aa8e5334f243b0b96c9b4 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 26 Nov 2020 17:03:33 +0100 Subject: [PATCH 1/2] Better account deletion (fixes #730) --- lemmy_api/src/comment.rs | 4 +-- lemmy_api/src/post.rs | 4 +-- lemmy_api/src/user.rs | 5 ++++ lemmy_db/src/schema.rs | 1 + lemmy_db/src/user.rs | 26 +++++++++++++++++-- .../2020-11-26-134531_delete_user/down.sql | 1 + .../2020-11-26-134531_delete_user/up.sql | 1 + 7 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 migrations/2020-11-26-134531_delete_user/down.sql create mode 100644 migrations/2020-11-26-134531_delete_user/up.sql diff --git a/lemmy_api/src/comment.rs b/lemmy_api/src/comment.rs index b1107d0d..e74fa808 100644 --- a/lemmy_api/src/comment.rs +++ b/lemmy_api/src/comment.rs @@ -782,9 +782,9 @@ impl Perform for ResolveCommentReport { let resolved = data.resolved; let resolve_fun = move |conn: &'_ _| { if resolved { - CommentReport::resolve(conn, report_id.clone(), user_id) + CommentReport::resolve(conn, report_id, user_id) } else { - CommentReport::unresolve(conn, report_id.clone(), user_id) + CommentReport::unresolve(conn, report_id, user_id) } }; diff --git a/lemmy_api/src/post.rs b/lemmy_api/src/post.rs index 707c8335..d5515b5c 100644 --- a/lemmy_api/src/post.rs +++ b/lemmy_api/src/post.rs @@ -839,9 +839,9 @@ impl Perform for ResolvePostReport { let resolved = data.resolved; let resolve_fun = move |conn: &'_ _| { if resolved { - PostReport::resolve(conn, report_id.clone(), user_id) + PostReport::resolve(conn, report_id, user_id) } else { - PostReport::unresolve(conn, report_id.clone(), user_id) + PostReport::unresolve(conn, report_id, user_id) } }; diff --git a/lemmy_api/src/user.rs b/lemmy_api/src/user.rs index 3b0b6d3f..0d96c2a2 100644 --- a/lemmy_api/src/user.rs +++ b/lemmy_api/src/user.rs @@ -890,6 +890,11 @@ impl Perform for DeleteAccount { return Err(APIError::err("couldnt_update_post").into()); } + blocking(context.pool(), move |conn| { + User_::delete_account(conn, user_id) + }) + .await??; + Ok(LoginResponse { jwt: data.auth.to_owned(), }) diff --git a/lemmy_db/src/schema.rs b/lemmy_db/src/schema.rs index 400c87d4..49bbc46f 100644 --- a/lemmy_db/src/schema.rs +++ b/lemmy_db/src/schema.rs @@ -467,6 +467,7 @@ table! { public_key -> Nullable, last_refreshed_at -> Timestamp, banner -> Nullable, + deleted -> Bool, } } diff --git a/lemmy_db/src/user.rs b/lemmy_db/src/user.rs index 378bbad9..e5fa9c18 100644 --- a/lemmy_db/src/user.rs +++ b/lemmy_db/src/user.rs @@ -37,6 +37,7 @@ pub struct User_ { pub public_key: Option, pub last_refreshed_at: chrono::NaiveDateTime, pub banner: Option, + pub deleted: bool, } #[derive(Insertable, AsChangeset, Clone)] @@ -70,7 +71,10 @@ pub struct UserForm { impl Crud for User_ { fn read(conn: &PgConnection, user_id: i32) -> Result { - user_.find(user_id).first::(conn) + user_ + .filter(deleted.eq(false)) + .find(user_id) + .first::(conn) } fn delete(conn: &PgConnection, user_id: i32) -> Result { diesel::delete(user_.find(user_id)).execute(conn) @@ -114,6 +118,7 @@ impl User_ { pub fn read_from_name(conn: &PgConnection, from_user_name: &str) -> Result { user_ .filter(local.eq(true)) + .filter(deleted.eq(false)) .filter(name.eq(from_user_name)) .first::(conn) } @@ -132,7 +137,10 @@ impl User_ { pub fn read_from_actor_id(conn: &PgConnection, object_id: &str) -> Result { use crate::schema::user_::dsl::*; - user_.filter(actor_id.eq(object_id)).first::(conn) + user_ + .filter(deleted.eq(false)) + .filter(actor_id.eq(object_id)) + .first::(conn) } pub fn find_by_email_or_username( @@ -148,6 +156,7 @@ impl User_ { pub fn find_by_username(conn: &PgConnection, username: &str) -> Result { user_ + .filter(deleted.eq(false)) .filter(local.eq(true)) .filter(name.ilike(username)) .first::(conn) @@ -155,6 +164,7 @@ impl User_ { pub fn find_by_email(conn: &PgConnection, from_email: &str) -> Result { user_ + .filter(deleted.eq(false)) .filter(local.eq(true)) .filter(email.eq(from_email)) .first::(conn) @@ -177,6 +187,18 @@ impl User_ { .set(user_form) .get_result::(conn) } + + pub fn delete_account(conn: &PgConnection, user_id: i32) -> Result { + diesel::update(user_.find(user_id)) + .set(( + preferred_username.eq::>(None), + email.eq::>(None), + matrix_user_id.eq::>(None), + deleted.eq(true), + updated.eq(naive_now()), + )) + .get_result::(conn) + } } #[cfg(test)] diff --git a/migrations/2020-11-26-134531_delete_user/down.sql b/migrations/2020-11-26-134531_delete_user/down.sql new file mode 100644 index 00000000..09b05878 --- /dev/null +++ b/migrations/2020-11-26-134531_delete_user/down.sql @@ -0,0 +1 @@ +ALTER TABLE user_ DROP COLUMN deleted; diff --git a/migrations/2020-11-26-134531_delete_user/up.sql b/migrations/2020-11-26-134531_delete_user/up.sql new file mode 100644 index 00000000..1d752c57 --- /dev/null +++ b/migrations/2020-11-26-134531_delete_user/up.sql @@ -0,0 +1 @@ +ALTER TABLE user_ ADD COLUMN deleted BOOLEAN DEFAULT FALSE NOT NULL; -- 2.40.1 From c2adaea5c10ac32d1d3c1c8c45382d2dac193ed1 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 27 Nov 2020 17:46:46 +0100 Subject: [PATCH 2/2] Try to implement federated user deletion (fixes #1284) --- lemmy_api/src/user.rs | 5 +++- lemmy_apub/src/activities/send/user.rs | 39 ++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/lemmy_api/src/user.rs b/lemmy_api/src/user.rs index 0d96c2a2..2dd0b271 100644 --- a/lemmy_api/src/user.rs +++ b/lemmy_api/src/user.rs @@ -13,7 +13,7 @@ use anyhow::Context; use bcrypt::verify; use captcha::{gen, Difficulty}; use chrono::Duration; -use lemmy_apub::ApubObjectType; +use lemmy_apub::{ActorType, ApubObjectType}; use lemmy_db::{ comment::*, comment_report::CommentReportView, @@ -877,6 +877,9 @@ impl Perform for DeleteAccount { return Err(APIError::err("password_incorrect").into()); } + // Send activity to delete user on federated instances + user.send_delete(context).await?; + // Comments let user_id = user.id; let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, user_id); diff --git a/lemmy_apub/src/activities/send/user.rs b/lemmy_apub/src/activities/send/user.rs index 2839d83b..81b33fcd 100644 --- a/lemmy_apub/src/activities/send/user.rs +++ b/lemmy_apub/src/activities/send/user.rs @@ -5,13 +5,16 @@ use crate::{ }; use activitystreams::{ activity::{ - kind::{FollowType, UndoType}, + kind::{DeleteType, FollowType, UndoType}, + Delete, Follow, Undo, }, base::{AnyBase, BaseExt, ExtendsExt}, object::ObjectExt, + public, }; +use itertools::Itertools; use lemmy_db::{ community::{Community, CommunityFollower, CommunityFollowerForm}, user::User_, @@ -105,8 +108,38 @@ impl ActorType for User_ { unimplemented!() } - async fn send_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> { - unimplemented!() + /// The user has deleted their account + async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError> { + let mut delete = Delete::new(self.actor_id()?, self.actor_id()?); + delete + .set_context(activitystreams::context()) + .set_id(generate_activity_id(DeleteType::Delete)?) + .set_to(public()) + // TODO: who is `cc`? all the communities the user has posted to? our inbox code can only + // handle activities for one activity. sending separate activities would work, but + // would also create a lot of traffic. + .set_many_ccs(vec![self.get_followers_url()?]); + + // TODO: should directly get distinct shared_inboxes from federated communites from db + // (once we store that) + let distinct_communities = blocking(context.pool(), move |conn| { + Community::distinct_federated_communities(conn) + }) + .await??; + let shared_inboxes = distinct_communities + .iter() + .map(|actor_id| Url::parse(actor_id).ok()) + .flatten() + .map(|url| url.host_str().map(|u| u.to_string())) + .flatten() + .unique() + .map(|c| format!("{}/inbox", c)) + .collect::>(); + // TODO: maybe we should add a separate function `send_activity_multi_dest()` + for s in &shared_inboxes { + send_activity_single_dest(delete.to_owned(), self, Url::parse(s)?, context).await?; + } + Ok(()) } async fn send_undo_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> { -- 2.40.1