parent
9ac1f46a2b
commit
8337eaefdd
12 changed files with 200 additions and 36 deletions
|
@ -58,6 +58,7 @@ import {
|
|||
CommentReportResponse,
|
||||
ListCommentReports,
|
||||
ListCommentReportsResponse,
|
||||
DeleteAccount,
|
||||
} from 'lemmy-js-client';
|
||||
|
||||
export interface API {
|
||||
|
@ -549,6 +550,16 @@ export async function saveUserSettings(
|
|||
return api.client.saveUserSettings(form);
|
||||
}
|
||||
|
||||
export async function deleteUser(
|
||||
api: API,
|
||||
): Promise<LoginResponse> {
|
||||
let form: DeleteAccount = {
|
||||
auth: api.auth,
|
||||
password
|
||||
};
|
||||
return api.client.deleteAccount(form);
|
||||
}
|
||||
|
||||
export async function getSite(
|
||||
api: API
|
||||
): Promise<GetSiteResponse> {
|
||||
|
|
|
@ -6,6 +6,15 @@ import {
|
|||
resolvePerson,
|
||||
saveUserSettings,
|
||||
getSite,
|
||||
createPost,
|
||||
gamma,
|
||||
resolveCommunity,
|
||||
createComment,
|
||||
resolveBetaCommunity,
|
||||
deleteUser,
|
||||
resolvePost,
|
||||
API,
|
||||
resolveComment,
|
||||
} from './shared';
|
||||
import {
|
||||
PersonViewSafe,
|
||||
|
@ -60,3 +69,33 @@ test('Set some user settings, check that they are federated', async () => {
|
|||
let betaPerson = (await resolvePerson(beta, apShortname)).person;
|
||||
assertUserFederation(alphaPerson, betaPerson);
|
||||
});
|
||||
|
||||
test('Delete user', async () => {
|
||||
let userRes = await registerUser(alpha);
|
||||
expect(userRes.jwt).toBeDefined();
|
||||
let user: API = {
|
||||
client: alpha.client,
|
||||
auth: userRes.jwt
|
||||
}
|
||||
|
||||
// make a local post and comment
|
||||
let alphaCommunity = (await resolveCommunity(user, '!main@lemmy-alpha:8541')).community;
|
||||
let localPost = (await createPost(user, alphaCommunity.community.id)).post_view.post;
|
||||
expect(localPost).toBeDefined();
|
||||
let localComment = (await createComment(user, localPost.id)).comment_view.comment;
|
||||
expect(localComment).toBeDefined();
|
||||
|
||||
// make a remote post and comment
|
||||
let betaCommunity = (await resolveBetaCommunity(user)).community;
|
||||
let remotePost = (await createPost(user, betaCommunity.community.id)).post_view.post;
|
||||
expect(remotePost).toBeDefined();
|
||||
let remoteComment = (await createComment(user, remotePost.id)).comment_view.comment;
|
||||
expect(remoteComment).toBeDefined();
|
||||
|
||||
await deleteUser(user);
|
||||
|
||||
expect((await resolvePost(alpha, localPost)).post).toBeUndefined();
|
||||
expect((await resolveComment(alpha, localComment)).comment).toBeUndefined();
|
||||
expect((await resolvePost(alpha, remotePost)).post).toBeUndefined();
|
||||
expect((await resolveComment(alpha, remoteComment)).comment).toBeUndefined();
|
||||
});
|
||||
|
|
|
@ -508,12 +508,8 @@ impl Perform for LeaveAdmin {
|
|||
let site_view = blocking(context.pool(), SiteView::read_local).await??;
|
||||
let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
|
||||
|
||||
let federated_instances = build_federated_instances(
|
||||
context.pool(),
|
||||
&context.settings().federation,
|
||||
&context.settings().hostname,
|
||||
)
|
||||
.await?;
|
||||
let federated_instances =
|
||||
build_federated_instances(context.pool(), &context.settings()).await?;
|
||||
|
||||
Ok(GetSiteResponse {
|
||||
site_view: Some(site_view),
|
||||
|
|
|
@ -13,6 +13,7 @@ use lemmy_db_schema::{
|
|||
community::Community,
|
||||
email_verification::{EmailVerification, EmailVerificationForm},
|
||||
password_reset_request::PasswordResetRequest,
|
||||
person::Person,
|
||||
person_block::PersonBlock,
|
||||
post::{Post, PostRead, PostReadForm},
|
||||
registration_application::RegistrationApplication,
|
||||
|
@ -34,7 +35,7 @@ use lemmy_db_views_actor::{
|
|||
use lemmy_utils::{
|
||||
claims::Claims,
|
||||
email::{send_email, translations::Lang},
|
||||
settings::structs::{FederationConfig, Settings},
|
||||
settings::structs::Settings,
|
||||
utils::generate_random_string,
|
||||
LemmyError,
|
||||
Sensitive,
|
||||
|
@ -295,9 +296,10 @@ pub async fn check_private_instance(
|
|||
#[tracing::instrument(skip_all)]
|
||||
pub async fn build_federated_instances(
|
||||
pool: &DbPool,
|
||||
federation_config: &FederationConfig,
|
||||
hostname: &str,
|
||||
settings: &Settings,
|
||||
) -> Result<Option<FederatedInstances>, LemmyError> {
|
||||
let federation_config = &settings.federation;
|
||||
let hostname = &settings.hostname;
|
||||
let federation = federation_config.to_owned();
|
||||
if federation.enabled {
|
||||
let distinct_communities = blocking(pool, move |conn| {
|
||||
|
@ -579,6 +581,24 @@ pub async fn remove_user_data_in_community(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_user_account(person_id: PersonId, pool: &DbPool) -> Result<(), LemmyError> {
|
||||
// Comments
|
||||
let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, person_id);
|
||||
blocking(pool, permadelete)
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
|
||||
|
||||
// Posts
|
||||
let permadelete = move |conn: &'_ _| Post::permadelete_for_creator(conn, person_id);
|
||||
blocking(pool, permadelete)
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_post"))?;
|
||||
|
||||
blocking(pool, move |conn| Person::delete_account(conn, person_id)).await??;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_image_has_local_domain(url: &Option<DbUrl>) -> Result<(), LemmyError> {
|
||||
if let Some(url) = url {
|
||||
let settings = Settings::get();
|
||||
|
|
|
@ -134,12 +134,8 @@ impl PerformCrud for GetSite {
|
|||
None
|
||||
};
|
||||
|
||||
let federated_instances = build_federated_instances(
|
||||
context.pool(),
|
||||
&context.settings().federation,
|
||||
&context.settings().hostname,
|
||||
)
|
||||
.await?;
|
||||
let federated_instances =
|
||||
build_federated_instances(context.pool(), &context.settings()).await?;
|
||||
|
||||
Ok(GetSiteResponse {
|
||||
site_view,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use bcrypt::verify;
|
||||
use lemmy_api_common::{blocking, get_local_user_view_from_jwt, person::*};
|
||||
use lemmy_db_schema::source::{comment::Comment, person::Person, post::Post};
|
||||
use lemmy_api_common::{delete_user_account, get_local_user_view_from_jwt, person::*};
|
||||
use lemmy_apub::protocol::activities::deletion::delete_user::DeleteUser;
|
||||
use lemmy_utils::{ConnectionId, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
||||
|
@ -30,23 +30,8 @@ impl PerformCrud for DeleteAccount {
|
|||
return Err(LemmyError::from_message("password_incorrect"));
|
||||
}
|
||||
|
||||
// Comments
|
||||
let person_id = local_user_view.person.id;
|
||||
let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, person_id);
|
||||
blocking(context.pool(), permadelete)
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
|
||||
|
||||
// Posts
|
||||
let permadelete = move |conn: &'_ _| Post::permadelete_for_creator(conn, person_id);
|
||||
blocking(context.pool(), permadelete)
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_post"))?;
|
||||
|
||||
blocking(context.pool(), move |conn| {
|
||||
Person::delete_account(conn, person_id)
|
||||
})
|
||||
.await??;
|
||||
delete_user_account(local_user_view.person.id, context.pool()).await?;
|
||||
DeleteUser::send(&local_user_view.person.into(), context).await?;
|
||||
|
||||
Ok(DeleteAccountResponse {})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"object": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||
"cc": [
|
||||
"http://enterprise.lemmy.ml/c/main"
|
||||
],
|
||||
"type": "Delete",
|
||||
"id": "http://ds9.lemmy.ml/activities/delete/f2abee48-c7bb-41d5-9e27-8775ff32db12"
|
||||
}
|
74
crates/apub/src/activities/deletion/delete_user.rs
Normal file
74
crates/apub/src/activities/deletion/delete_user.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
|
||||
objects::person::ApubPerson,
|
||||
protocol::activities::deletion::delete_user::DeleteUser,
|
||||
};
|
||||
use activitystreams_kinds::{activity::DeleteType, public};
|
||||
use lemmy_api_common::{blocking, delete_user_account};
|
||||
use lemmy_apub_lib::{
|
||||
data::Data,
|
||||
object_id::ObjectId,
|
||||
traits::ActivityHandler,
|
||||
verify::verify_urls_match,
|
||||
};
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
||||
/// This can be separate from Delete activity because it doesn't need to be handled in shared inbox
|
||||
/// (cause instance actor doesn't have shared inbox).
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for DeleteUser {
|
||||
type DataType = LemmyContext;
|
||||
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &[])?;
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
verify_urls_match(self.actor.inner(), self.object.inner())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor = self
|
||||
.actor
|
||||
.dereference(context, context.client(), request_counter)
|
||||
.await?;
|
||||
delete_user_account(actor.id, context.pool()).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DeleteUser {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send(actor: &ApubPerson, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
let actor_id = ObjectId::new(actor.actor_id.clone());
|
||||
let id = generate_activity_id(
|
||||
DeleteType::Delete,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let delete = DeleteUser {
|
||||
actor: actor_id.clone(),
|
||||
to: vec![public()],
|
||||
object: actor_id,
|
||||
kind: DeleteType::Delete,
|
||||
id: id.clone(),
|
||||
cc: vec![],
|
||||
};
|
||||
|
||||
let remote_sites = blocking(context.pool(), Site::read_remote_sites).await??;
|
||||
let inboxes = remote_sites
|
||||
.into_iter()
|
||||
.map(|s| s.inbox_url.into())
|
||||
.collect();
|
||||
send_lemmy_activity(context, &delete, &id, actor, inboxes, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -49,6 +49,7 @@ use std::ops::Deref;
|
|||
use url::Url;
|
||||
|
||||
pub mod delete;
|
||||
pub mod delete_user;
|
||||
pub mod undo_delete;
|
||||
|
||||
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
post::CreateOrUpdatePost,
|
||||
private_message::CreateOrUpdatePrivateMessage,
|
||||
},
|
||||
deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||
deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
|
||||
following::{
|
||||
accept::AcceptFollowCommunity,
|
||||
follow::FollowCommunity,
|
||||
|
@ -87,9 +87,11 @@ pub enum AnnouncableActivities {
|
|||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
||||
#[serde(untagged)]
|
||||
#[activity_handler(LemmyContext)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum SiteInboxActivities {
|
||||
BlockUser(BlockUser),
|
||||
UndoBlockUser(UndoBlockUser),
|
||||
DeleteUser(DeleteUser),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
|
|
24
crates/apub/src/protocol/activities/deletion/delete_user.rs
Normal file
24
crates/apub/src/protocol/activities/deletion/delete_user.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use crate::objects::person::ApubPerson;
|
||||
use activitystreams_kinds::activity::DeleteType;
|
||||
use lemmy_apub_lib::object_id::ObjectId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use url::Url;
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteUser {
|
||||
pub(crate) actor: ObjectId<ApubPerson>,
|
||||
#[serde(deserialize_with = "crate::deserialize_one_or_many")]
|
||||
pub(crate) to: Vec<Url>,
|
||||
pub(crate) object: ObjectId<ApubPerson>,
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: DeleteType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(deserialize_with = "crate::deserialize_one_or_many")]
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub(crate) cc: Vec<Url>,
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
pub mod delete;
|
||||
pub mod delete_user;
|
||||
pub mod undo_delete;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::protocol::{
|
||||
activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||
activities::deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
|
||||
|
@ -23,5 +24,8 @@ mod tests {
|
|||
"assets/lemmy/activities/deletion/undo_delete_private_message.json",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
test_parse_lemmy_item::<DeleteUser>("assets/lemmy/activities/deletion/delete_user.json")
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue