parent
9ac1f46a2b
commit
8337eaefdd
12 changed files with 200 additions and 36 deletions
|
@ -58,6 +58,7 @@ import {
|
||||||
CommentReportResponse,
|
CommentReportResponse,
|
||||||
ListCommentReports,
|
ListCommentReports,
|
||||||
ListCommentReportsResponse,
|
ListCommentReportsResponse,
|
||||||
|
DeleteAccount,
|
||||||
} from 'lemmy-js-client';
|
} from 'lemmy-js-client';
|
||||||
|
|
||||||
export interface API {
|
export interface API {
|
||||||
|
@ -549,6 +550,16 @@ export async function saveUserSettings(
|
||||||
return api.client.saveUserSettings(form);
|
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(
|
export async function getSite(
|
||||||
api: API
|
api: API
|
||||||
): Promise<GetSiteResponse> {
|
): Promise<GetSiteResponse> {
|
||||||
|
|
|
@ -6,6 +6,15 @@ import {
|
||||||
resolvePerson,
|
resolvePerson,
|
||||||
saveUserSettings,
|
saveUserSettings,
|
||||||
getSite,
|
getSite,
|
||||||
|
createPost,
|
||||||
|
gamma,
|
||||||
|
resolveCommunity,
|
||||||
|
createComment,
|
||||||
|
resolveBetaCommunity,
|
||||||
|
deleteUser,
|
||||||
|
resolvePost,
|
||||||
|
API,
|
||||||
|
resolveComment,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import {
|
import {
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
|
@ -60,3 +69,33 @@ test('Set some user settings, check that they are federated', async () => {
|
||||||
let betaPerson = (await resolvePerson(beta, apShortname)).person;
|
let betaPerson = (await resolvePerson(beta, apShortname)).person;
|
||||||
assertUserFederation(alphaPerson, betaPerson);
|
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 site_view = blocking(context.pool(), SiteView::read_local).await??;
|
||||||
let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
|
let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
|
||||||
|
|
||||||
let federated_instances = build_federated_instances(
|
let federated_instances =
|
||||||
context.pool(),
|
build_federated_instances(context.pool(), &context.settings()).await?;
|
||||||
&context.settings().federation,
|
|
||||||
&context.settings().hostname,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site_view: Some(site_view),
|
site_view: Some(site_view),
|
||||||
|
|
|
@ -13,6 +13,7 @@ use lemmy_db_schema::{
|
||||||
community::Community,
|
community::Community,
|
||||||
email_verification::{EmailVerification, EmailVerificationForm},
|
email_verification::{EmailVerification, EmailVerificationForm},
|
||||||
password_reset_request::PasswordResetRequest,
|
password_reset_request::PasswordResetRequest,
|
||||||
|
person::Person,
|
||||||
person_block::PersonBlock,
|
person_block::PersonBlock,
|
||||||
post::{Post, PostRead, PostReadForm},
|
post::{Post, PostRead, PostReadForm},
|
||||||
registration_application::RegistrationApplication,
|
registration_application::RegistrationApplication,
|
||||||
|
@ -34,7 +35,7 @@ use lemmy_db_views_actor::{
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
claims::Claims,
|
claims::Claims,
|
||||||
email::{send_email, translations::Lang},
|
email::{send_email, translations::Lang},
|
||||||
settings::structs::{FederationConfig, Settings},
|
settings::structs::Settings,
|
||||||
utils::generate_random_string,
|
utils::generate_random_string,
|
||||||
LemmyError,
|
LemmyError,
|
||||||
Sensitive,
|
Sensitive,
|
||||||
|
@ -295,9 +296,10 @@ pub async fn check_private_instance(
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn build_federated_instances(
|
pub async fn build_federated_instances(
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
federation_config: &FederationConfig,
|
settings: &Settings,
|
||||||
hostname: &str,
|
|
||||||
) -> Result<Option<FederatedInstances>, LemmyError> {
|
) -> Result<Option<FederatedInstances>, LemmyError> {
|
||||||
|
let federation_config = &settings.federation;
|
||||||
|
let hostname = &settings.hostname;
|
||||||
let federation = federation_config.to_owned();
|
let federation = federation_config.to_owned();
|
||||||
if federation.enabled {
|
if federation.enabled {
|
||||||
let distinct_communities = blocking(pool, move |conn| {
|
let distinct_communities = blocking(pool, move |conn| {
|
||||||
|
@ -579,6 +581,24 @@ pub async fn remove_user_data_in_community(
|
||||||
Ok(())
|
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> {
|
pub fn check_image_has_local_domain(url: &Option<DbUrl>) -> Result<(), LemmyError> {
|
||||||
if let Some(url) = url {
|
if let Some(url) = url {
|
||||||
let settings = Settings::get();
|
let settings = Settings::get();
|
||||||
|
|
|
@ -134,12 +134,8 @@ impl PerformCrud for GetSite {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let federated_instances = build_federated_instances(
|
let federated_instances =
|
||||||
context.pool(),
|
build_federated_instances(context.pool(), &context.settings()).await?;
|
||||||
&context.settings().federation,
|
|
||||||
&context.settings().hostname,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site_view,
|
site_view,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::PerformCrud;
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use bcrypt::verify;
|
use bcrypt::verify;
|
||||||
use lemmy_api_common::{blocking, get_local_user_view_from_jwt, person::*};
|
use lemmy_api_common::{delete_user_account, get_local_user_view_from_jwt, person::*};
|
||||||
use lemmy_db_schema::source::{comment::Comment, person::Person, post::Post};
|
use lemmy_apub::protocol::activities::deletion::delete_user::DeleteUser;
|
||||||
use lemmy_utils::{ConnectionId, LemmyError};
|
use lemmy_utils::{ConnectionId, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
|
||||||
|
@ -30,23 +30,8 @@ impl PerformCrud for DeleteAccount {
|
||||||
return Err(LemmyError::from_message("password_incorrect"));
|
return Err(LemmyError::from_message("password_incorrect"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comments
|
delete_user_account(local_user_view.person.id, context.pool()).await?;
|
||||||
let person_id = local_user_view.person.id;
|
DeleteUser::send(&local_user_view.person.into(), context).await?;
|
||||||
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??;
|
|
||||||
|
|
||||||
Ok(DeleteAccountResponse {})
|
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;
|
use url::Url;
|
||||||
|
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
|
pub mod delete_user;
|
||||||
pub mod undo_delete;
|
pub mod undo_delete;
|
||||||
|
|
||||||
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
|
/// 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,
|
post::CreateOrUpdatePost,
|
||||||
private_message::CreateOrUpdatePrivateMessage,
|
private_message::CreateOrUpdatePrivateMessage,
|
||||||
},
|
},
|
||||||
deletion::{delete::Delete, undo_delete::UndoDelete},
|
deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
|
||||||
following::{
|
following::{
|
||||||
accept::AcceptFollowCommunity,
|
accept::AcceptFollowCommunity,
|
||||||
follow::FollowCommunity,
|
follow::FollowCommunity,
|
||||||
|
@ -87,9 +87,11 @@ pub enum AnnouncableActivities {
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
#[activity_handler(LemmyContext)]
|
#[activity_handler(LemmyContext)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub enum SiteInboxActivities {
|
pub enum SiteInboxActivities {
|
||||||
BlockUser(BlockUser),
|
BlockUser(BlockUser),
|
||||||
UndoBlockUser(UndoBlockUser),
|
UndoBlockUser(UndoBlockUser),
|
||||||
|
DeleteUser(DeleteUser),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[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;
|
||||||
|
pub mod delete_user;
|
||||||
pub mod undo_delete;
|
pub mod undo_delete;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::protocol::{
|
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,
|
tests::test_parse_lemmy_item,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,5 +24,8 @@ mod tests {
|
||||||
"assets/lemmy/activities/deletion/undo_delete_private_message.json",
|
"assets/lemmy/activities/deletion/undo_delete_private_message.json",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
test_parse_lemmy_item::<DeleteUser>("assets/lemmy/activities/deletion/delete_user.json")
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue