Merge /site_inbox
into /inbox
, remove unique constraint for inboxes (#4138)
* Merge /site_inbox into /inbox (fixes #4137) Get rid of different inboxes, only use /inbox Remove shared_inbox_url db columns add code migration move to db migration, fixes machete fix sql drop inbox url unique constraints Dont create auth cookie in backend (#4136) dont change individual inboxes to shared inbox Dont send comment reply to user who has community blocked. Fixes #3684 (#4096) * Dont send comment reply to user who has community blocked. Fixes #3684 * Adding source instance block check. * Adding api test. * Addressing PR comments. * move site inbox rewrite to db * fix test * clippy * clippy 2 * fix test
This commit is contained in:
parent
a53892d2bb
commit
a00313e680
19 changed files with 154 additions and 169 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2563,7 +2563,6 @@ version = "0.19.0-rc.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"encoding",
|
"encoding",
|
||||||
"enum-map",
|
"enum-map",
|
||||||
|
|
|
@ -12,12 +12,12 @@ import {
|
||||||
createComment,
|
createComment,
|
||||||
resolveBetaCommunity,
|
resolveBetaCommunity,
|
||||||
deleteUser,
|
deleteUser,
|
||||||
resolvePost,
|
|
||||||
resolveComment,
|
|
||||||
saveUserSettingsFederated,
|
saveUserSettingsFederated,
|
||||||
setupLogins,
|
setupLogins,
|
||||||
alphaUrl,
|
alphaUrl,
|
||||||
saveUserSettings,
|
saveUserSettings,
|
||||||
|
getPost,
|
||||||
|
getComments,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { LemmyHttp, SaveUserSettings } from "lemmy-js-client";
|
import { LemmyHttp, SaveUserSettings } from "lemmy-js-client";
|
||||||
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
||||||
|
@ -103,18 +103,22 @@ test("Delete user", async () => {
|
||||||
|
|
||||||
await deleteUser(user);
|
await deleteUser(user);
|
||||||
|
|
||||||
await expect(resolvePost(alpha, localPost)).rejects.toBe(
|
// check that posts and comments are marked as deleted on other instances.
|
||||||
"couldnt_find_object",
|
// use get methods to avoid refetching from origin instance
|
||||||
|
expect((await getPost(alpha, localPost.id)).post_view.post.deleted).toBe(
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
await expect(resolveComment(alpha, localComment)).rejects.toBe(
|
expect((await getPost(alpha, remotePost.id)).post_view.post.deleted).toBe(
|
||||||
"couldnt_find_object",
|
true,
|
||||||
);
|
|
||||||
await expect(resolvePost(alpha, remotePost)).rejects.toBe(
|
|
||||||
"couldnt_find_object",
|
|
||||||
);
|
|
||||||
await expect(resolveComment(alpha, remoteComment)).rejects.toBe(
|
|
||||||
"couldnt_find_object",
|
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
(await getComments(alpha, localComment.post_id)).comments[0].comment
|
||||||
|
.deleted,
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
(await getComments(alpha, remoteComment.post_id)).comments[0].comment
|
||||||
|
.deleted,
|
||||||
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Requests with invalid auth should be treated as unauthenticated", async () => {
|
test("Requests with invalid auth should be treated as unauthenticated", async () => {
|
||||||
|
|
|
@ -56,7 +56,6 @@ webpage = { version = "1.6", default-features = false, features = [
|
||||||
"serde",
|
"serde",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
encoding = { version = "0.2.33", optional = true }
|
encoding = { version = "0.2.33", optional = true }
|
||||||
anyhow = { workspace = true }
|
|
||||||
futures = { workspace = true, optional = true }
|
futures = { workspace = true, optional = true }
|
||||||
uuid = { workspace = true, optional = true }
|
uuid = { workspace = true, optional = true }
|
||||||
tokio = { workspace = true, optional = true }
|
tokio = { workspace = true, optional = true }
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
||||||
request::purge_image_from_pictrs,
|
request::purge_image_from_pictrs,
|
||||||
site::{FederatedInstances, InstanceWithFederationState},
|
site::{FederatedInstances, InstanceWithFederationState},
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
|
||||||
use chrono::{DateTime, Days, Local, TimeZone, Utc};
|
use chrono::{DateTime, Days, Local, TimeZone, Utc};
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -34,7 +33,6 @@ use lemmy_db_views_actor::structs::{
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
email::{send_email, translations::Lang},
|
email::{send_email, translations::Lang},
|
||||||
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
location_info,
|
|
||||||
rate_limit::{ActionType, BucketConfig},
|
rate_limit::{ActionType, BucketConfig},
|
||||||
settings::structs::Settings,
|
settings::structs::Settings,
|
||||||
utils::slurs::build_slur_regex,
|
utils::slurs::build_slur_regex,
|
||||||
|
@ -786,24 +784,8 @@ pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||||
Ok(Url::parse(&format!("{actor_id}/inbox"))?.into())
|
Ok(Url::parse(&format!("{actor_id}/inbox"))?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_site_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
pub fn generate_shared_inbox_url(settings: &Settings) -> Result<DbUrl, LemmyError> {
|
||||||
let mut actor_id: Url = actor_id.clone().into();
|
let url = format!("{}/inbox", settings.get_protocol_and_hostname());
|
||||||
actor_id.set_path("site_inbox");
|
|
||||||
Ok(actor_id.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
|
|
||||||
let actor_id: Url = actor_id.clone().into();
|
|
||||||
let url = format!(
|
|
||||||
"{}://{}{}/inbox",
|
|
||||||
&actor_id.scheme(),
|
|
||||||
&actor_id.host_str().context(location_info!())?,
|
|
||||||
if let Some(port) = actor_id.port() {
|
|
||||||
format!(":{port}")
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Ok(Url::parse(&url)?.into())
|
Ok(Url::parse(&url)?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ pub async fn create_community(
|
||||||
.public_key(keypair.public_key)
|
.public_key(keypair.public_key)
|
||||||
.followers_url(Some(generate_followers_url(&community_actor_id)?))
|
.followers_url(Some(generate_followers_url(&community_actor_id)?))
|
||||||
.inbox_url(Some(generate_inbox_url(&community_actor_id)?))
|
.inbox_url(Some(generate_inbox_url(&community_actor_id)?))
|
||||||
.shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?))
|
.shared_inbox_url(Some(generate_shared_inbox_url(context.settings())?))
|
||||||
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
||||||
.instance_id(site_view.site.instance_id)
|
.instance_id(site_view.site.instance_id)
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -4,7 +4,7 @@ use actix_web::web::{Data, Json};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
site::{CreateSite, SiteResponse},
|
site::{CreateSite, SiteResponse},
|
||||||
utils::{generate_site_inbox_url, is_admin, local_site_rate_limit_to_rate_limit_config},
|
utils::{generate_shared_inbox_url, is_admin, local_site_rate_limit_to_rate_limit_config},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::DbUrl,
|
newtypes::DbUrl,
|
||||||
|
@ -47,7 +47,7 @@ pub async fn create_site(
|
||||||
validate_create_payload(&local_site, &data)?;
|
validate_create_payload(&local_site, &data)?;
|
||||||
|
|
||||||
let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
|
let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
|
||||||
let inbox_url = Some(generate_site_inbox_url(&actor_id)?);
|
let inbox_url = Some(generate_shared_inbox_url(context.settings())?);
|
||||||
let keypair = generate_actor_keypair()?;
|
let keypair = generate_actor_keypair()?;
|
||||||
|
|
||||||
let site_form = SiteUpdateForm {
|
let site_form = SiteUpdateForm {
|
||||||
|
|
|
@ -113,7 +113,7 @@ pub async fn register(
|
||||||
.private_key(Some(actor_keypair.private_key))
|
.private_key(Some(actor_keypair.private_key))
|
||||||
.public_key(actor_keypair.public_key)
|
.public_key(actor_keypair.public_key)
|
||||||
.inbox_url(Some(generate_inbox_url(&actor_id)?))
|
.inbox_url(Some(generate_inbox_url(&actor_id)?))
|
||||||
.shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?))
|
.shared_inbox_url(Some(generate_shared_inbox_url(context.settings())?))
|
||||||
.instance_id(site_view.site.instance_id)
|
.instance_id(site_view.site.instance_id)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,14 @@ impl ActivityHandler for Delete {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
} else {
|
} else {
|
||||||
receive_delete_action(self.object.id(), &self.actor, true, context).await
|
receive_delete_action(
|
||||||
|
self.object.id(),
|
||||||
|
&self.actor,
|
||||||
|
true,
|
||||||
|
self.remove_data,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,6 +101,7 @@ impl Delete {
|
||||||
summary,
|
summary,
|
||||||
id,
|
id,
|
||||||
audience: community.map(|c| c.actor_id.clone().into()),
|
audience: community.map(|c| c.actor_id.clone().into()),
|
||||||
|
remove_data: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,6 +172,7 @@ pub(in crate::activities) async fn receive_remove_action(
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
DeletableObjects::PrivateMessage(_) => unimplemented!(),
|
DeletableObjects::PrivateMessage(_) => unimplemented!(),
|
||||||
|
DeletableObjects::Person { .. } => unimplemented!(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
use crate::{
|
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
|
|
||||||
insert_received_activity,
|
|
||||||
objects::person::ApubPerson,
|
|
||||||
protocol::activities::deletion::delete_user::DeleteUser,
|
|
||||||
};
|
|
||||||
use activitypub_federation::{
|
|
||||||
config::Data,
|
|
||||||
kinds::{activity::DeleteType, public},
|
|
||||||
protocol::verification::verify_urls_match,
|
|
||||||
traits::{ActivityHandler, Actor},
|
|
||||||
};
|
|
||||||
use lemmy_api_common::{context::LemmyContext, utils::purge_user_account};
|
|
||||||
use lemmy_db_schema::source::{activity::ActivitySendTargets, person::Person};
|
|
||||||
use lemmy_utils::error::LemmyError;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
pub async fn delete_user(
|
|
||||||
person: Person,
|
|
||||||
delete_content: bool,
|
|
||||||
context: Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let actor: ApubPerson = person.into();
|
|
||||||
|
|
||||||
let id = generate_activity_id(
|
|
||||||
DeleteType::Delete,
|
|
||||||
&context.settings().get_protocol_and_hostname(),
|
|
||||||
)?;
|
|
||||||
let delete = DeleteUser {
|
|
||||||
actor: actor.id().into(),
|
|
||||||
to: vec![public()],
|
|
||||||
object: actor.id().into(),
|
|
||||||
kind: DeleteType::Delete,
|
|
||||||
id: id.clone(),
|
|
||||||
cc: vec![],
|
|
||||||
remove_data: Some(delete_content),
|
|
||||||
};
|
|
||||||
|
|
||||||
let inboxes = ActivitySendTargets::to_all_instances();
|
|
||||||
|
|
||||||
send_lemmy_activity(&context, delete, &actor, inboxes, true).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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]
|
|
||||||
impl ActivityHandler for DeleteUser {
|
|
||||||
type DataType = LemmyContext;
|
|
||||||
type Error = LemmyError;
|
|
||||||
|
|
||||||
fn id(&self) -> &Url {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn actor(&self) -> &Url {
|
|
||||||
self.actor.inner()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
|
||||||
insert_received_activity(&self.id, context).await?;
|
|
||||||
verify_is_public(&self.to, &[])?;
|
|
||||||
verify_person(&self.actor, context).await?;
|
|
||||||
verify_urls_match(self.actor.inner(), self.object.inner())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
|
||||||
let actor = self.actor.dereference(context).await?;
|
|
||||||
if self.remove_data.unwrap_or(false) {
|
|
||||||
purge_user_account(actor.id, context).await?;
|
|
||||||
} else {
|
|
||||||
Person::delete_account(&mut context.pool(), actor.id).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -24,10 +24,10 @@ use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
fetch::object_id::ObjectId,
|
fetch::object_id::ObjectId,
|
||||||
kinds::public,
|
kinds::public,
|
||||||
protocol::verification::verify_domains_match,
|
protocol::verification::{verify_domains_match, verify_urls_match},
|
||||||
traits::{Actor, Object},
|
traits::{Actor, Object},
|
||||||
};
|
};
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::{context::LemmyContext, utils::purge_user_account};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::CommunityId,
|
newtypes::CommunityId,
|
||||||
source::{
|
source::{
|
||||||
|
@ -45,7 +45,6 @@ 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
|
||||||
|
@ -135,8 +134,26 @@ pub(crate) async fn send_apub_delete_private_message(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn send_apub_delete_user(
|
||||||
|
person: Person,
|
||||||
|
remove_data: bool,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let person: ApubPerson = person.into();
|
||||||
|
|
||||||
|
let deletable = DeletableObjects::Person(person.clone());
|
||||||
|
let mut delete: Delete = Delete::new(&person, deletable, public(), None, None, &context)?;
|
||||||
|
delete.remove_data = Some(remove_data);
|
||||||
|
|
||||||
|
let inboxes = ActivitySendTargets::to_all_instances();
|
||||||
|
|
||||||
|
send_lemmy_activity(&context, delete, &person, inboxes, true).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub enum DeletableObjects {
|
pub enum DeletableObjects {
|
||||||
Community(ApubCommunity),
|
Community(ApubCommunity),
|
||||||
|
Person(ApubPerson),
|
||||||
Comment(ApubComment),
|
Comment(ApubComment),
|
||||||
Post(ApubPost),
|
Post(ApubPost),
|
||||||
PrivateMessage(ApubPrivateMessage),
|
PrivateMessage(ApubPrivateMessage),
|
||||||
|
@ -151,6 +168,9 @@ impl DeletableObjects {
|
||||||
if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
|
if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
|
||||||
return Ok(DeletableObjects::Community(c));
|
return Ok(DeletableObjects::Community(c));
|
||||||
}
|
}
|
||||||
|
if let Some(p) = ApubPerson::read_from_id(ap_id.clone(), context).await? {
|
||||||
|
return Ok(DeletableObjects::Person(p));
|
||||||
|
}
|
||||||
if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
|
if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
|
||||||
return Ok(DeletableObjects::Post(p));
|
return Ok(DeletableObjects::Post(p));
|
||||||
}
|
}
|
||||||
|
@ -166,6 +186,7 @@ impl DeletableObjects {
|
||||||
pub(crate) fn id(&self) -> Url {
|
pub(crate) fn id(&self) -> Url {
|
||||||
match self {
|
match self {
|
||||||
DeletableObjects::Community(c) => c.id(),
|
DeletableObjects::Community(c) => c.id(),
|
||||||
|
DeletableObjects::Person(p) => p.id(),
|
||||||
DeletableObjects::Comment(c) => c.ap_id.clone().into(),
|
DeletableObjects::Comment(c) => c.ap_id.clone().into(),
|
||||||
DeletableObjects::Post(p) => p.ap_id.clone().into(),
|
DeletableObjects::Post(p) => p.ap_id.clone().into(),
|
||||||
DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
|
DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
|
||||||
|
@ -191,6 +212,11 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||||
// community deletion is always a mod (or admin) action
|
// community deletion is always a mod (or admin) action
|
||||||
verify_mod_action(&activity.actor, &community, context).await?;
|
verify_mod_action(&activity.actor, &community, context).await?;
|
||||||
}
|
}
|
||||||
|
DeletableObjects::Person(person) => {
|
||||||
|
verify_is_public(&activity.to, &[])?;
|
||||||
|
verify_person(&activity.actor, context).await?;
|
||||||
|
verify_urls_match(person.actor_id.inner(), activity.object.id())?;
|
||||||
|
}
|
||||||
DeletableObjects::Post(p) => {
|
DeletableObjects::Post(p) => {
|
||||||
verify_is_public(&activity.to, &[])?;
|
verify_is_public(&activity.to, &[])?;
|
||||||
verify_delete_post_or_comment(
|
verify_delete_post_or_comment(
|
||||||
|
@ -245,6 +271,7 @@ async fn receive_delete_action(
|
||||||
object: &Url,
|
object: &Url,
|
||||||
actor: &ObjectId<ApubPerson>,
|
actor: &ObjectId<ApubPerson>,
|
||||||
deleted: bool,
|
deleted: bool,
|
||||||
|
do_purge_user_account: Option<bool>,
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
match DeletableObjects::read_from_db(object, context).await? {
|
match DeletableObjects::read_from_db(object, context).await? {
|
||||||
|
@ -266,6 +293,13 @@ async fn receive_delete_action(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
DeletableObjects::Person(person) => {
|
||||||
|
if do_purge_user_account.unwrap_or(false) {
|
||||||
|
purge_user_account(person.id, context).await?;
|
||||||
|
} else {
|
||||||
|
Person::delete_account(&mut context.pool(), person.id).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
DeletableObjects::Post(post) => {
|
DeletableObjects::Post(post) => {
|
||||||
if deleted != post.deleted {
|
if deleted != post.deleted {
|
||||||
Post::update(
|
Post::update(
|
||||||
|
|
|
@ -58,7 +58,7 @@ impl ActivityHandler for UndoDelete {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
} else {
|
} else {
|
||||||
receive_delete_action(self.object.object.id(), &self.actor, false, context).await
|
receive_delete_action(self.object.object.id(), &self.actor, false, None, context).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,7 @@ impl UndoDelete {
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
DeletableObjects::PrivateMessage(_) => unimplemented!(),
|
DeletableObjects::PrivateMessage(_) => unimplemented!(),
|
||||||
|
DeletableObjects::Person { .. } => unimplemented!(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
create_or_update::private_message::send_create_or_update_pm,
|
create_or_update::private_message::send_create_or_update_pm,
|
||||||
deletion::{
|
deletion::{
|
||||||
delete_user::delete_user,
|
|
||||||
send_apub_delete_in_community,
|
send_apub_delete_in_community,
|
||||||
send_apub_delete_in_community_new,
|
send_apub_delete_in_community_new,
|
||||||
send_apub_delete_private_message,
|
send_apub_delete_private_message,
|
||||||
|
send_apub_delete_user,
|
||||||
DeletableObjects,
|
DeletableObjects,
|
||||||
},
|
},
|
||||||
voting::send_like_activity,
|
voting::send_like_activity,
|
||||||
|
@ -330,7 +330,7 @@ pub async fn match_outgoing_activities(
|
||||||
DeletePrivateMessage(person, pm, deleted) => {
|
DeletePrivateMessage(person, pm, deleted) => {
|
||||||
send_apub_delete_private_message(&person.into(), pm, deleted, context).await
|
send_apub_delete_private_message(&person.into(), pm, deleted, context).await
|
||||||
}
|
}
|
||||||
DeleteUser(person, delete_content) => delete_user(person, delete_content, context).await,
|
DeleteUser(person, remove_data) => send_apub_delete_user(person, remove_data, context).await,
|
||||||
CreateReport(url, actor, community, reason) => {
|
CreateReport(url, actor, community, reason) => {
|
||||||
Report::send(ObjectId::from(url), actor, community, reason, context).await
|
Report::send(ObjectId::from(url), actor, community, reason, context).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
note::CreateOrUpdateNote,
|
note::CreateOrUpdateNote,
|
||||||
page::CreateOrUpdatePage,
|
page::CreateOrUpdatePage,
|
||||||
},
|
},
|
||||||
deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
|
deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow},
|
following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow},
|
||||||
voting::{undo_vote::UndoVote, vote::Vote},
|
voting::{undo_vote::UndoVote, vote::Vote},
|
||||||
},
|
},
|
||||||
|
@ -98,16 +98,6 @@ pub enum AnnouncableActivities {
|
||||||
Page(Page),
|
Page(Page),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
#[enum_delegate::implement(ActivityHandler)]
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
|
||||||
pub enum SiteInboxActivities {
|
|
||||||
BlockUser(BlockUser),
|
|
||||||
UndoBlockUser(UndoBlockUser),
|
|
||||||
DeleteUser(DeleteUser),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl InCommunity for AnnouncableActivities {
|
impl InCommunity for AnnouncableActivities {
|
||||||
#[tracing::instrument(skip(self, context))]
|
#[tracing::instrument(skip(self, context))]
|
||||||
|
@ -138,7 +128,7 @@ mod tests {
|
||||||
#![allow(clippy::indexing_slicing)]
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
activity_lists::{GroupInboxActivities, PersonInboxActivities, SiteInboxActivities},
|
activity_lists::{GroupInboxActivities, PersonInboxActivities, SharedInboxActivities},
|
||||||
protocol::tests::{test_json, test_parse_lemmy_item},
|
protocol::tests::{test_json, test_parse_lemmy_item},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -168,8 +158,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_site_inbox() {
|
fn test_shared_inbox() {
|
||||||
test_parse_lemmy_item::<SiteInboxActivities>(
|
test_parse_lemmy_item::<SharedInboxActivities>(
|
||||||
"assets/lemmy/activities/deletion/delete_user.json",
|
"assets/lemmy/activities/deletion/delete_user.json",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::http::{
|
||||||
person::{get_apub_person_http, get_apub_person_outbox, person_inbox},
|
person::{get_apub_person_http, get_apub_person_outbox, person_inbox},
|
||||||
post::get_apub_post,
|
post::get_apub_post,
|
||||||
shared_inbox,
|
shared_inbox,
|
||||||
site::{get_apub_site_http, get_apub_site_inbox, get_apub_site_outbox},
|
site::{get_apub_site_http, get_apub_site_outbox},
|
||||||
};
|
};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
guard::{Guard, GuardContext},
|
guard::{Guard, GuardContext},
|
||||||
|
@ -58,8 +58,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
.guard(InboxRequestGuard)
|
.guard(InboxRequestGuard)
|
||||||
.route("/c/{community_name}/inbox", web::post().to(community_inbox))
|
.route("/c/{community_name}/inbox", web::post().to(community_inbox))
|
||||||
.route("/u/{user_name}/inbox", web::post().to(person_inbox))
|
.route("/u/{user_name}/inbox", web::post().to(person_inbox))
|
||||||
.route("/inbox", web::post().to(shared_inbox))
|
.route("/inbox", web::post().to(shared_inbox)),
|
||||||
.route("/site_inbox", web::post().to(get_apub_site_inbox)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activity_lists::SiteInboxActivities,
|
|
||||||
http::create_apub_response,
|
http::create_apub_response,
|
||||||
objects::{instance::ApubSite, person::ApubPerson},
|
objects::instance::ApubSite,
|
||||||
protocol::collections::empty_outbox::EmptyOutbox,
|
protocol::collections::empty_outbox::EmptyOutbox,
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{config::Data, traits::Object};
|
||||||
actix_web::inbox::receive_activity,
|
use actix_web::HttpResponse;
|
||||||
config::Data,
|
|
||||||
protocol::context::WithContext,
|
|
||||||
traits::Object,
|
|
||||||
};
|
|
||||||
use actix_web::{web::Bytes, HttpRequest, HttpResponse};
|
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::context::LemmyContext;
|
||||||
use lemmy_db_views::structs::SiteView;
|
use lemmy_db_views::structs::SiteView;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
@ -36,15 +30,3 @@ pub(crate) async fn get_apub_site_outbox(
|
||||||
let outbox = EmptyOutbox::new(Url::parse(&outbox_id)?)?;
|
let outbox = EmptyOutbox::new(Url::parse(&outbox_id)?)?;
|
||||||
create_apub_response(&outbox)
|
create_apub_response(&outbox)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub async fn get_apub_site_inbox(
|
|
||||||
request: HttpRequest,
|
|
||||||
body: Bytes,
|
|
||||||
data: Data<LemmyContext>,
|
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
|
||||||
receive_activity::<WithContext<SiteInboxActivities>, ApubPerson, LemmyContext>(
|
|
||||||
request, body, &data,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
|
@ -40,6 +40,9 @@ pub struct Delete {
|
||||||
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
|
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
|
||||||
/// deleting their own content.
|
/// deleting their own content.
|
||||||
pub(crate) summary: Option<String>,
|
pub(crate) summary: Option<String>,
|
||||||
|
/// Nonstandard field, only valid if object refers to a Person. If present, all content from the
|
||||||
|
/// user should be deleted along with the account
|
||||||
|
pub(crate) remove_data: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -52,6 +55,7 @@ impl InCommunity for Delete {
|
||||||
post.community_id
|
post.community_id
|
||||||
}
|
}
|
||||||
DeletableObjects::Post(p) => p.community_id,
|
DeletableObjects::Post(p) => p.community_id,
|
||||||
|
DeletableObjects::Person(_) => return Err(anyhow!("Person is not part of community").into()),
|
||||||
DeletableObjects::PrivateMessage(_) => {
|
DeletableObjects::PrivateMessage(_) => {
|
||||||
return Err(anyhow!("Private message is not part of community").into())
|
return Err(anyhow!("Private message is not part of community").into())
|
||||||
}
|
}
|
||||||
|
|
26
migrations/2023-11-07-135409_inbox_unique/down.sql
Normal file
26
migrations/2023-11-07-135409_inbox_unique/down.sql
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
ALTER TABLE person
|
||||||
|
ADD CONSTRAINT idx_person_inbox_url UNIQUE (inbox_url);
|
||||||
|
|
||||||
|
ALTER TABLE community
|
||||||
|
ADD CONSTRAINT idx_community_inbox_url UNIQUE (inbox_url);
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
site
|
||||||
|
SET
|
||||||
|
inbox_url = inbox_query.inbox
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
format('https://%s/site_inbox', DOMAIN) AS inbox
|
||||||
|
FROM
|
||||||
|
instance,
|
||||||
|
site,
|
||||||
|
local_site
|
||||||
|
WHERE
|
||||||
|
instance.id = site.instance_id
|
||||||
|
AND local_site.id = site.id) AS inbox_query,
|
||||||
|
instance,
|
||||||
|
local_site
|
||||||
|
WHERE
|
||||||
|
instance.id = site.instance_id
|
||||||
|
AND local_site.id = site.id;
|
||||||
|
|
30
migrations/2023-11-07-135409_inbox_unique/up.sql
Normal file
30
migrations/2023-11-07-135409_inbox_unique/up.sql
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
-- drop unique constraints for inbox columns
|
||||||
|
ALTER TABLE person
|
||||||
|
DROP CONSTRAINT idx_person_inbox_url;
|
||||||
|
|
||||||
|
ALTER TABLE community
|
||||||
|
DROP CONSTRAINT idx_community_inbox_url;
|
||||||
|
|
||||||
|
-- change site inbox path from /inbox to /site_inbox
|
||||||
|
-- we dont have any way here to set the correct protocol (http or https) according to tls_enabled, or set
|
||||||
|
-- the correct port in case of debugging
|
||||||
|
UPDATE
|
||||||
|
site
|
||||||
|
SET
|
||||||
|
inbox_url = inbox_query.inbox
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
format('https://%s/inbox', DOMAIN) AS inbox
|
||||||
|
FROM
|
||||||
|
instance,
|
||||||
|
site,
|
||||||
|
local_site
|
||||||
|
WHERE
|
||||||
|
instance.id = site.instance_id
|
||||||
|
AND local_site.id = site.id) AS inbox_query,
|
||||||
|
instance,
|
||||||
|
local_site
|
||||||
|
WHERE
|
||||||
|
instance.id = site.instance_id
|
||||||
|
AND local_site.id = site.id;
|
||||||
|
|
|
@ -15,7 +15,6 @@ use lemmy_api_common::{
|
||||||
generate_inbox_url,
|
generate_inbox_url,
|
||||||
generate_local_apub_endpoint,
|
generate_local_apub_endpoint,
|
||||||
generate_shared_inbox_url,
|
generate_shared_inbox_url,
|
||||||
generate_site_inbox_url,
|
|
||||||
EndpointType,
|
EndpointType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -50,8 +49,8 @@ pub async fn run_advanced_migrations(
|
||||||
comment_updates_2020_04_03(pool, protocol_and_hostname).await?;
|
comment_updates_2020_04_03(pool, protocol_and_hostname).await?;
|
||||||
private_message_updates_2020_05_05(pool, protocol_and_hostname).await?;
|
private_message_updates_2020_05_05(pool, protocol_and_hostname).await?;
|
||||||
post_thumbnail_url_updates_2020_07_27(pool, protocol_and_hostname).await?;
|
post_thumbnail_url_updates_2020_07_27(pool, protocol_and_hostname).await?;
|
||||||
apub_columns_2021_02_02(pool).await?;
|
apub_columns_2021_02_02(pool, settings).await?;
|
||||||
instance_actor_2022_01_28(pool, protocol_and_hostname).await?;
|
instance_actor_2022_01_28(pool, protocol_and_hostname, settings).await?;
|
||||||
regenerate_public_keys_2022_07_05(pool).await?;
|
regenerate_public_keys_2022_07_05(pool).await?;
|
||||||
initialize_local_site_2022_10_10(pool, settings).await?;
|
initialize_local_site_2022_10_10(pool, settings).await?;
|
||||||
|
|
||||||
|
@ -283,7 +282,10 @@ async fn post_thumbnail_url_updates_2020_07_27(
|
||||||
|
|
||||||
/// We are setting inbox and follower URLs for local and remote actors alike, because for now
|
/// We are setting inbox and follower URLs for local and remote actors alike, because for now
|
||||||
/// all federated instances are also Lemmy and use the same URL scheme.
|
/// all federated instances are also Lemmy and use the same URL scheme.
|
||||||
async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>) -> Result<(), LemmyError> {
|
async fn apub_columns_2021_02_02(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
info!("Running apub_columns_2021_02_02");
|
info!("Running apub_columns_2021_02_02");
|
||||||
{
|
{
|
||||||
|
@ -295,7 +297,7 @@ async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>) -> Result<(), LemmyError
|
||||||
|
|
||||||
for p in &persons {
|
for p in &persons {
|
||||||
let inbox_url_ = generate_inbox_url(&p.actor_id)?;
|
let inbox_url_ = generate_inbox_url(&p.actor_id)?;
|
||||||
let shared_inbox_url_ = generate_shared_inbox_url(&p.actor_id)?;
|
let shared_inbox_url_ = generate_shared_inbox_url(settings)?;
|
||||||
diesel::update(person.find(p.id))
|
diesel::update(person.find(p.id))
|
||||||
.set((
|
.set((
|
||||||
inbox_url.eq(inbox_url_),
|
inbox_url.eq(inbox_url_),
|
||||||
|
@ -321,7 +323,7 @@ async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>) -> Result<(), LemmyError
|
||||||
for c in &communities {
|
for c in &communities {
|
||||||
let followers_url_ = generate_followers_url(&c.actor_id)?;
|
let followers_url_ = generate_followers_url(&c.actor_id)?;
|
||||||
let inbox_url_ = generate_inbox_url(&c.actor_id)?;
|
let inbox_url_ = generate_inbox_url(&c.actor_id)?;
|
||||||
let shared_inbox_url_ = generate_shared_inbox_url(&c.actor_id)?;
|
let shared_inbox_url_ = generate_shared_inbox_url(settings)?;
|
||||||
diesel::update(community.find(c.id))
|
diesel::update(community.find(c.id))
|
||||||
.set((
|
.set((
|
||||||
followers_url.eq(followers_url_),
|
followers_url.eq(followers_url_),
|
||||||
|
@ -343,6 +345,7 @@ async fn apub_columns_2021_02_02(pool: &mut DbPool<'_>) -> Result<(), LemmyError
|
||||||
async fn instance_actor_2022_01_28(
|
async fn instance_actor_2022_01_28(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
protocol_and_hostname: &str,
|
protocol_and_hostname: &str,
|
||||||
|
settings: &Settings,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
info!("Running instance_actor_2021_09_29");
|
info!("Running instance_actor_2021_09_29");
|
||||||
if let Ok(site_view) = SiteView::read_local(pool).await {
|
if let Ok(site_view) = SiteView::read_local(pool).await {
|
||||||
|
@ -356,7 +359,7 @@ async fn instance_actor_2022_01_28(
|
||||||
let site_form = SiteUpdateForm {
|
let site_form = SiteUpdateForm {
|
||||||
actor_id: Some(actor_id.clone().into()),
|
actor_id: Some(actor_id.clone().into()),
|
||||||
last_refreshed_at: Some(naive_now()),
|
last_refreshed_at: Some(naive_now()),
|
||||||
inbox_url: Some(generate_site_inbox_url(&actor_id.into())?),
|
inbox_url: Some(generate_shared_inbox_url(settings)?),
|
||||||
private_key: Some(Some(key_pair.private_key)),
|
private_key: Some(Some(key_pair.private_key)),
|
||||||
public_key: Some(key_pair.public_key),
|
public_key: Some(key_pair.public_key),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -462,7 +465,7 @@ async fn initialize_local_site_2022_10_10(
|
||||||
.private_key(Some(person_keypair.private_key))
|
.private_key(Some(person_keypair.private_key))
|
||||||
.public_key(person_keypair.public_key)
|
.public_key(person_keypair.public_key)
|
||||||
.inbox_url(Some(generate_inbox_url(&person_actor_id)?))
|
.inbox_url(Some(generate_inbox_url(&person_actor_id)?))
|
||||||
.shared_inbox_url(Some(generate_shared_inbox_url(&person_actor_id)?))
|
.shared_inbox_url(Some(generate_shared_inbox_url(settings)?))
|
||||||
.build();
|
.build();
|
||||||
let person_inserted = Person::create(pool, &person_form).await?;
|
let person_inserted = Person::create(pool, &person_form).await?;
|
||||||
|
|
||||||
|
@ -490,7 +493,7 @@ async fn initialize_local_site_2022_10_10(
|
||||||
.instance_id(instance.id)
|
.instance_id(instance.id)
|
||||||
.actor_id(Some(site_actor_id.clone().into()))
|
.actor_id(Some(site_actor_id.clone().into()))
|
||||||
.last_refreshed_at(Some(naive_now()))
|
.last_refreshed_at(Some(naive_now()))
|
||||||
.inbox_url(Some(generate_site_inbox_url(&site_actor_id.into())?))
|
.inbox_url(Some(generate_shared_inbox_url(settings)?))
|
||||||
.private_key(Some(site_key_pair.private_key))
|
.private_key(Some(site_key_pair.private_key))
|
||||||
.public_key(Some(site_key_pair.public_key))
|
.public_key(Some(site_key_pair.public_key))
|
||||||
.build();
|
.build();
|
||||||
|
|
Loading…
Reference in a new issue