mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
Activitypub crate rewrite (#2782)
* update activitypub-federation crate to 0.4.0 * fixes * apub compiles! * everything compiling! * almost done, federated follow failing * some test fixes * use release * add code back in
This commit is contained in:
parent
6bc49bdd70
commit
6f513793cb
52
Cargo.lock
generated
52
Cargo.lock
generated
@ -4,26 +4,33 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "activitypub_federation"
|
||||
version = "0.3.5"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59fbd2b7fb0aea9bdd738fc1441d34d3e7b585d60b42ed63deeac289c872f119"
|
||||
checksum = "b52228e706f380074b0722dae97f9c8274264026dbd3aa16d20466758995b6f4"
|
||||
dependencies = [
|
||||
"activitystreams-kinds",
|
||||
"actix-rt",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"background-jobs",
|
||||
"base64",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"derive_builder 0.11.2",
|
||||
"derive_builder 0.12.0",
|
||||
"displaydoc",
|
||||
"dyn-clone",
|
||||
"enum_delegate",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-signature-normalization-actix",
|
||||
"http-signature-normalization",
|
||||
"http-signature-normalization-reqwest",
|
||||
"httpdate",
|
||||
"itertools",
|
||||
"once_cell",
|
||||
"openssl",
|
||||
"pin-project-lite",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest-middleware",
|
||||
"serde",
|
||||
@ -695,9 +702,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.1"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||
|
||||
[[package]]
|
||||
name = "bytestring"
|
||||
@ -1232,11 +1239,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.11.2"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3"
|
||||
checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
|
||||
dependencies = [
|
||||
"derive_builder_macro 0.11.2",
|
||||
"derive_builder_macro 0.12.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1253,9 +1260,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.11.2"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4"
|
||||
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
|
||||
dependencies = [
|
||||
"darling 0.14.1",
|
||||
"proc-macro2 1.0.47",
|
||||
@ -1275,11 +1282,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.11.2"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68"
|
||||
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
|
||||
dependencies = [
|
||||
"derive_builder_core 0.11.2",
|
||||
"derive_builder_core 0.12.0",
|
||||
"syn 1.0.103",
|
||||
]
|
||||
|
||||
@ -1418,6 +1425,17 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.47",
|
||||
"quote 1.0.21",
|
||||
"syn 1.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlv-list"
|
||||
version = "0.3.0"
|
||||
@ -2451,7 +2469,6 @@ name = "lemmy_apub"
|
||||
version = "0.17.1"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"activitystreams-kinds",
|
||||
"actix-rt",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
@ -2557,6 +2574,7 @@ dependencies = [
|
||||
name = "lemmy_routes"
|
||||
version = "0.17.1"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -3998,9 +4016,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -59,7 +59,7 @@ lemmy_routes = { version = "=0.17.1", path = "./crates/routes" }
|
||||
lemmy_db_views = { version = "=0.17.1", path = "./crates/db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.17.1", path = "./crates/db_views_actor" }
|
||||
lemmy_db_views_moderator = { version = "=0.17.1", path = "./crates/db_views_moderator" }
|
||||
activitypub_federation = "0.3.5"
|
||||
activitypub_federation = { version = "0.4.0", default-features = false, features = ["actix-web"] }
|
||||
diesel = "2.0.2"
|
||||
diesel_migrations = "2.0.0"
|
||||
diesel-async = "0.1.1"
|
||||
|
@ -7,12 +7,12 @@ use lemmy_utils::{
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LemmyContext {
|
||||
pool: DbPool,
|
||||
chat_server: Arc<ChatServer>,
|
||||
client: ClientWithMiddleware,
|
||||
settings: Settings,
|
||||
secret: Secret,
|
||||
client: Arc<ClientWithMiddleware>,
|
||||
secret: Arc<Secret>,
|
||||
rate_limit_cell: RateLimitCell,
|
||||
}
|
||||
|
||||
@ -21,16 +21,14 @@ impl LemmyContext {
|
||||
pool: DbPool,
|
||||
chat_server: Arc<ChatServer>,
|
||||
client: ClientWithMiddleware,
|
||||
settings: Settings,
|
||||
secret: Secret,
|
||||
rate_limit_cell: RateLimitCell,
|
||||
) -> LemmyContext {
|
||||
LemmyContext {
|
||||
pool,
|
||||
chat_server,
|
||||
client,
|
||||
settings,
|
||||
secret,
|
||||
client: Arc::new(client),
|
||||
secret: Arc::new(secret),
|
||||
rate_limit_cell,
|
||||
}
|
||||
}
|
||||
@ -53,16 +51,3 @@ impl LemmyContext {
|
||||
&self.rate_limit_cell
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for LemmyContext {
|
||||
fn clone(&self) -> Self {
|
||||
LemmyContext {
|
||||
pool: self.pool.clone(),
|
||||
chat_server: self.chat_server.clone(),
|
||||
client: self.client.clone(),
|
||||
settings: self.settings.clone(),
|
||||
secret: self.secret.clone(),
|
||||
rate_limit_cell: self.rate_limit_cell.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ fn html_to_site_metadata(html_bytes: &[u8]) -> Result<SiteMetadata, LemmyError>
|
||||
let first_line = html
|
||||
.trim_start()
|
||||
.lines()
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| LemmyError::from_message("No lines in html"))?
|
||||
.to_lowercase();
|
||||
|
@ -73,7 +73,7 @@ pub struct SearchResponse {
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct ResolveObject {
|
||||
pub q: String,
|
||||
pub auth: Option<Sensitive<String>>,
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
|
@ -142,6 +142,7 @@ pub async fn mark_post_as_unread(
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read"))
|
||||
}
|
||||
|
||||
// TODO: this should simply take LemmyContext as param
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn get_local_user_view_from_jwt(
|
||||
jwt: &str,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::PerformCrud;
|
||||
use activitypub_federation::core::signatures::generate_actor_keypair;
|
||||
use activitypub_federation::http_signatures::generate_actor_keypair;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
community::{CommunityResponse, CreateCommunity},
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{site::check_application_question, PerformCrud};
|
||||
use activitypub_federation::core::signatures::generate_actor_keypair;
|
||||
use activitypub_federation::http_signatures::generate_actor_keypair;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::PerformCrud;
|
||||
use activitypub_federation::core::signatures::generate_actor_keypair;
|
||||
use activitypub_federation::http_signatures::generate_actor_keypair;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
|
@ -41,7 +41,6 @@ once_cell = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
html2md = "0.2.13"
|
||||
serde_with = "1.14.0"
|
||||
activitystreams-kinds = "0.2.1"
|
||||
http-signature-normalization-actix = { version = "0.6.1", default-features = false, features = ["server", "sha-2"] }
|
||||
enum_delegate = "0.2.0"
|
||||
|
||||
|
@ -9,18 +9,16 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
||||
protocol::activities::block::block_user::BlockUser,
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
config::Data,
|
||||
kinds::{activity::BlockType, public},
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::{ActivityHandler, Actor},
|
||||
utils::verify_domains_match,
|
||||
};
|
||||
use activitystreams_kinds::{activity::BlockType, public};
|
||||
use anyhow::anyhow;
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::{
|
||||
@ -51,17 +49,17 @@ impl BlockUser {
|
||||
remove_data: Option<bool>,
|
||||
reason: Option<String>,
|
||||
expires: Option<NaiveDateTime>,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<BlockUser, LemmyError> {
|
||||
let audience = if let SiteOrCommunity::Community(c) = target {
|
||||
Some(ObjectId::new(c.actor_id()))
|
||||
Some(c.id().into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(BlockUser {
|
||||
actor: ObjectId::new(mod_.actor_id()),
|
||||
actor: mod_.id().into(),
|
||||
to: vec![public()],
|
||||
object: ObjectId::new(user.actor_id()),
|
||||
object: user.id().into(),
|
||||
cc: generate_cc(target, context.pool()).await?,
|
||||
target: target.id(),
|
||||
kind: BlockType::Block,
|
||||
@ -84,7 +82,7 @@ impl BlockUser {
|
||||
remove_data: bool,
|
||||
reason: Option<String>,
|
||||
expires: Option<NaiveDateTime>,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let block = BlockUser::new(
|
||||
target,
|
||||
@ -111,7 +109,7 @@ impl BlockUser {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for BlockUser {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -125,17 +123,9 @@ impl ActivityHandler for BlockUser {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
match self
|
||||
.target
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?
|
||||
{
|
||||
match self.target.dereference(context).await? {
|
||||
SiteOrCommunity::Site(site) => {
|
||||
let domain = self.object.inner().domain().expect("url needs domain");
|
||||
if context.settings().hostname == domain {
|
||||
@ -144,43 +134,24 @@ impl ActivityHandler for BlockUser {
|
||||
);
|
||||
}
|
||||
// site ban can only target a user who is on the same instance as the actor (admin)
|
||||
verify_domains_match(&site.actor_id(), self.actor.inner())?;
|
||||
verify_domains_match(&site.actor_id(), self.object.inner())?;
|
||||
verify_domains_match(&site.id(), self.actor.inner())?;
|
||||
verify_domains_match(&site.id(), self.object.inner())?;
|
||||
}
|
||||
SiteOrCommunity::Community(community) => {
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
verify_mod_action(
|
||||
&self.actor,
|
||||
self.object.inner(),
|
||||
community.id,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
verify_mod_action(&self.actor, self.object.inner(), community.id, context).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let expires = self.expires.map(|u| u.naive_local());
|
||||
let mod_person = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let blocked_person = self
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let target = self
|
||||
.target
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let mod_person = self.actor.dereference(context).await?;
|
||||
let blocked_person = self.object.dereference(context).await?;
|
||||
let target = self.target.dereference(context).await?;
|
||||
match target {
|
||||
SiteOrCommunity::Site(_site) => {
|
||||
let blocked_person = Person::update(
|
||||
|
@ -4,10 +4,13 @@ use crate::{
|
||||
activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
||||
objects::{group::Group, instance::Instance},
|
||||
},
|
||||
ActorType,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
traits::{Actor, Object},
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::{
|
||||
community::{BanFromCommunity, BanFromCommunityResponse},
|
||||
@ -41,11 +44,10 @@ pub enum InstanceOrGroup {
|
||||
Group(Group),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for SiteOrCommunity {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for SiteOrCommunity {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = InstanceOrGroup;
|
||||
type DbType = ();
|
||||
type Kind = InstanceOrGroup;
|
||||
type Error = LemmyError;
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
@ -57,62 +59,51 @@ impl ApubObject for SiteOrCommunity {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
data: &Self::DataType,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let site = ApubSite::read_from_apub_id(object_id.clone(), data).await?;
|
||||
let site = ApubSite::read_from_id(object_id.clone(), data).await?;
|
||||
Ok(match site {
|
||||
Some(o) => Some(SiteOrCommunity::Site(o)),
|
||||
None => ApubCommunity::read_from_apub_id(object_id, data)
|
||||
None => ApubCommunity::read_from_id(object_id, data)
|
||||
.await?
|
||||
.map(SiteOrCommunity::Community),
|
||||
})
|
||||
}
|
||||
|
||||
async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
|
||||
async fn delete(self, _data: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
apub: &Self::ApubType,
|
||||
apub: &Self::Kind,
|
||||
expected_domain: &Url,
|
||||
data: &Self::DataType,
|
||||
request_counter: &mut i32,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
match apub {
|
||||
InstanceOrGroup::Instance(i) => {
|
||||
ApubSite::verify(i, expected_domain, data, request_counter).await
|
||||
}
|
||||
InstanceOrGroup::Group(g) => {
|
||||
ApubCommunity::verify(g, expected_domain, data, request_counter).await
|
||||
}
|
||||
InstanceOrGroup::Instance(i) => ApubSite::verify(i, expected_domain, data).await,
|
||||
InstanceOrGroup::Group(g) => ApubCommunity::verify(g, expected_domain, data).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
apub: Self::ApubType,
|
||||
data: &Self::DataType,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<Self, LemmyError>
|
||||
async fn from_json(apub: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, LemmyError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok(match apub {
|
||||
InstanceOrGroup::Instance(p) => {
|
||||
SiteOrCommunity::Site(ApubSite::from_apub(p, data, request_counter).await?)
|
||||
}
|
||||
InstanceOrGroup::Instance(p) => SiteOrCommunity::Site(ApubSite::from_json(p, data).await?),
|
||||
InstanceOrGroup::Group(n) => {
|
||||
SiteOrCommunity::Community(ApubCommunity::from_apub(n, data, request_counter).await?)
|
||||
SiteOrCommunity::Community(ApubCommunity::from_json(n, data).await?)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -121,8 +112,8 @@ impl ApubObject for SiteOrCommunity {
|
||||
impl SiteOrCommunity {
|
||||
fn id(&self) -> ObjectId<SiteOrCommunity> {
|
||||
match self {
|
||||
SiteOrCommunity::Site(s) => ObjectId::new(s.actor_id.clone()),
|
||||
SiteOrCommunity::Community(c) => ObjectId::new(c.actor_id.clone()),
|
||||
SiteOrCommunity::Site(s) => ObjectId::from(s.actor_id.clone()),
|
||||
SiteOrCommunity::Community(c) => ObjectId::from(c.actor_id.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,18 +125,18 @@ async fn generate_cc(target: &SiteOrCommunity, pool: &DbPool) -> Result<Vec<Url>
|
||||
.into_iter()
|
||||
.map(|s| s.actor_id.into())
|
||||
.collect(),
|
||||
SiteOrCommunity::Community(c) => vec![c.actor_id()],
|
||||
SiteOrCommunity::Community(c) => vec![c.id()],
|
||||
})
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for BanPerson {
|
||||
type Response = BanPersonResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -182,14 +173,14 @@ impl SendActivity for BanPerson {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for BanFromCommunity {
|
||||
type Response = BanFromCommunityResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
|
@ -7,18 +7,16 @@ use crate::{
|
||||
verify_is_public,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
||||
protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
config::Data,
|
||||
kinds::{activity::UndoType, public},
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::{ActivityHandler, Actor},
|
||||
utils::verify_domains_match,
|
||||
};
|
||||
use activitystreams_kinds::{activity::UndoType, public};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
@ -38,11 +36,11 @@ impl UndoBlockUser {
|
||||
user: &ApubPerson,
|
||||
mod_: &ApubPerson,
|
||||
reason: Option<String>,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let block = BlockUser::new(target, user, mod_, None, reason, None, context).await?;
|
||||
let audience = if let SiteOrCommunity::Community(c) = target {
|
||||
Some(ObjectId::new(c.actor_id()))
|
||||
Some(c.id().into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -52,7 +50,7 @@ impl UndoBlockUser {
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let undo = UndoBlockUser {
|
||||
actor: ObjectId::new(mod_.actor_id()),
|
||||
actor: mod_.id().into(),
|
||||
to: vec![public()],
|
||||
object: block,
|
||||
cc: generate_cc(target, context.pool()).await?,
|
||||
@ -75,7 +73,7 @@ impl UndoBlockUser {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for UndoBlockUser {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -89,40 +87,20 @@ impl ActivityHandler for UndoBlockUser {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
self.object.verify(context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let instance = local_instance(context).await;
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let expires = self.object.expires.map(|u| u.naive_local());
|
||||
let mod_person = self
|
||||
.actor
|
||||
.dereference(context, instance, request_counter)
|
||||
.await?;
|
||||
let blocked_person = self
|
||||
.object
|
||||
.object
|
||||
.dereference(context, instance, request_counter)
|
||||
.await?;
|
||||
match self
|
||||
.object
|
||||
.target
|
||||
.dereference(context, instance, request_counter)
|
||||
.await?
|
||||
{
|
||||
let mod_person = self.actor.dereference(context).await?;
|
||||
let blocked_person = self.object.object.dereference(context).await?;
|
||||
match self.object.target.dereference(context).await? {
|
||||
SiteOrCommunity::Site(_site) => {
|
||||
let blocked_person = Person::update(
|
||||
context.pool(),
|
||||
|
@ -14,17 +14,18 @@ use crate::{
|
||||
IdOrNestedObject,
|
||||
InCommunity,
|
||||
},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
|
||||
use activitystreams_kinds::{activity::AnnounceType, public};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
kinds::{activity::AnnounceType, public},
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde_json::Value;
|
||||
use tracing::debug;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for RawAnnouncableActivities {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -38,35 +39,27 @@ impl ActivityHandler for RawAnnouncableActivities {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
_data: &Data<Self::DataType>,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<(), Self::Error> {
|
||||
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
data: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), Self::Error> {
|
||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
let activity: AnnouncableActivities = self.clone().try_into()?;
|
||||
// This is only for sending, not receiving so we reject it.
|
||||
if let AnnouncableActivities::Page(_) = activity {
|
||||
return Err(LemmyError::from_message("Cant receive page"));
|
||||
}
|
||||
let community = activity.community(data, &mut 0).await?;
|
||||
let actor_id = ObjectId::new(activity.actor().clone());
|
||||
let community = activity.community(data).await?;
|
||||
let actor_id = activity.actor().clone().into();
|
||||
|
||||
// verify and receive activity
|
||||
activity.verify(data, request_counter).await?;
|
||||
activity.receive(data, request_counter).await?;
|
||||
activity.verify(data).await?;
|
||||
activity.receive(data).await?;
|
||||
|
||||
// send to community followers
|
||||
if community.local {
|
||||
verify_person_in_community(&actor_id, &community, data, &mut 0).await?;
|
||||
verify_person_in_community(&actor_id, &community, data).await?;
|
||||
AnnounceActivity::send(self, &community, data).await?;
|
||||
}
|
||||
Ok(())
|
||||
@ -77,10 +70,10 @@ impl AnnounceActivity {
|
||||
pub(crate) fn new(
|
||||
object: RawAnnouncableActivities,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<AnnounceActivity, LemmyError> {
|
||||
Ok(AnnounceActivity {
|
||||
actor: ObjectId::new(community.actor_id()),
|
||||
actor: community.id().into(),
|
||||
to: vec![public()],
|
||||
object: IdOrNestedObject::NestedObject(object),
|
||||
cc: vec![community.followers_url.clone().into()],
|
||||
@ -96,7 +89,7 @@ impl AnnounceActivity {
|
||||
pub async fn send(
|
||||
object: RawAnnouncableActivities,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let announce = AnnounceActivity::new(object.clone(), community, context)?;
|
||||
let inboxes = community.get_follower_inboxes(context).await?;
|
||||
@ -109,7 +102,10 @@ impl AnnounceActivity {
|
||||
// Hack: need to convert Page into a format which can be sent as activity, which requires
|
||||
// adding actor field.
|
||||
let announcable_page = RawAnnouncableActivities {
|
||||
id: c.object.id.clone().into_inner(),
|
||||
id: generate_activity_id(
|
||||
AnnounceType::Announce,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?,
|
||||
actor: c.actor.clone().into_inner(),
|
||||
other: serde_json::to_value(c.object)?
|
||||
.as_object()
|
||||
@ -123,7 +119,7 @@ impl AnnounceActivity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for AnnounceActivity {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -137,44 +133,23 @@ impl ActivityHandler for AnnounceActivity {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
_context: &Data<LemmyContext>,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let object: AnnouncableActivities = self
|
||||
.object
|
||||
.object(context, request_counter)
|
||||
.await?
|
||||
.try_into()?;
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let object: AnnouncableActivities = self.object.object(context).await?.try_into()?;
|
||||
// This is only for sending, not receiving so we reject it.
|
||||
if let AnnouncableActivities::Page(_) = object {
|
||||
return Err(LemmyError::from_message("Cant receive page"));
|
||||
}
|
||||
|
||||
// we have to verify this here in order to avoid fetching the object twice over http
|
||||
object.verify(context, request_counter).await?;
|
||||
|
||||
let object_value = serde_json::to_value(&object)?;
|
||||
let insert = insert_activity(object.id(), object_value, false, true, context.pool()).await?;
|
||||
if !insert {
|
||||
debug!(
|
||||
"Received duplicate activity in announce {}",
|
||||
object.id().to_string()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
object.receive(context, request_counter).await
|
||||
// verify here in order to avoid fetching the object twice over http
|
||||
object.verify(context).await?;
|
||||
object.receive(context).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||
protocol::{
|
||||
activities::{
|
||||
@ -17,15 +17,14 @@ use crate::{
|
||||
},
|
||||
InCommunity,
|
||||
},
|
||||
ActorType,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::{activity::AddType, public},
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use activitystreams_kinds::{activity::AddType, public};
|
||||
use lemmy_api_common::{
|
||||
community::{AddModToCommunity, AddModToCommunityResponse},
|
||||
context::LemmyContext,
|
||||
@ -51,21 +50,21 @@ impl CollectionAdd {
|
||||
community: &ApubCommunity,
|
||||
added_mod: &ApubPerson,
|
||||
actor: &ApubPerson,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let id = generate_activity_id(
|
||||
AddType::Add,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let add = CollectionAdd {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
to: vec![public()],
|
||||
object: added_mod.actor_id(),
|
||||
object: added_mod.id(),
|
||||
target: generate_moderators_url(&community.actor_id)?.into(),
|
||||
cc: vec![community.actor_id()],
|
||||
cc: vec![community.id()],
|
||||
kind: AddType::Add,
|
||||
id: id.clone(),
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
};
|
||||
|
||||
let activity = AnnouncableActivities::CollectionAdd(add);
|
||||
@ -77,28 +76,28 @@ impl CollectionAdd {
|
||||
community: &ApubCommunity,
|
||||
featured_post: &ApubPost,
|
||||
actor: &ApubPerson,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let id = generate_activity_id(
|
||||
AddType::Add,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let add = CollectionAdd {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
to: vec![public()],
|
||||
object: featured_post.ap_id.clone().into(),
|
||||
target: generate_featured_url(&community.actor_id)?.into(),
|
||||
cc: vec![community.actor_id()],
|
||||
cc: vec![community.id()],
|
||||
kind: AddType::Add,
|
||||
id: id.clone(),
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
};
|
||||
let activity = AnnouncableActivities::CollectionAdd(add);
|
||||
send_activity_in_community(activity, actor, community, vec![], true, context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for CollectionAdd {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -112,37 +111,23 @@ impl ActivityHandler for CollectionAdd {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
verify_mod_action(
|
||||
&self.actor,
|
||||
&self.object,
|
||||
community.id,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
verify_mod_action(&self.actor, &self.object, community.id, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let (community, collection_type) =
|
||||
Community::get_by_collection_url(context.pool(), &self.target.into()).await?;
|
||||
match collection_type {
|
||||
CollectionType::Moderators => {
|
||||
let new_mod = ObjectId::<ApubPerson>::new(self.object)
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
let new_mod = ObjectId::<ApubPerson>::from(self.object)
|
||||
.dereference(context)
|
||||
.await?;
|
||||
|
||||
// If we had to refetch the community while parsing the activity, then the new mod has already
|
||||
@ -158,10 +143,7 @@ impl ActivityHandler for CollectionAdd {
|
||||
CommunityModerator::join(context.pool(), &form).await?;
|
||||
|
||||
// write mod log
|
||||
let actor = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let actor = self.actor.dereference(context).await?;
|
||||
let form = ModAddCommunityForm {
|
||||
mod_person_id: actor.id,
|
||||
other_person_id: new_mod.id,
|
||||
@ -173,8 +155,8 @@ impl ActivityHandler for CollectionAdd {
|
||||
// TODO: send websocket notification about added mod
|
||||
}
|
||||
CollectionType::Featured => {
|
||||
let post = ObjectId::<ApubPost>::new(self.object)
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
let post = ObjectId::<ApubPost>::from(self.object)
|
||||
.dereference(context)
|
||||
.await?;
|
||||
let form = PostUpdateForm::builder()
|
||||
.featured_community(Some(true))
|
||||
@ -186,14 +168,14 @@ impl ActivityHandler for CollectionAdd {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for AddModToCommunity {
|
||||
type Response = AddModToCommunityResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -223,14 +205,14 @@ impl SendActivity for AddModToCommunity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for FeaturePost {
|
||||
type Response = PostResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
|
@ -7,17 +7,16 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||
protocol::{activities::community::collection_remove::CollectionRemove, InCommunity},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::{activity::RemoveType, public},
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use activitystreams_kinds::{activity::RemoveType, public};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::{generate_featured_url, generate_moderators_url},
|
||||
@ -40,21 +39,21 @@ impl CollectionRemove {
|
||||
community: &ApubCommunity,
|
||||
removed_mod: &ApubPerson,
|
||||
actor: &ApubPerson,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let id = generate_activity_id(
|
||||
RemoveType::Remove,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let remove = CollectionRemove {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
to: vec![public()],
|
||||
object: ObjectId::new(removed_mod.actor_id()),
|
||||
object: removed_mod.id(),
|
||||
target: generate_moderators_url(&community.actor_id)?.into(),
|
||||
id: id.clone(),
|
||||
cc: vec![community.actor_id()],
|
||||
cc: vec![community.id()],
|
||||
kind: RemoveType::Remove,
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
};
|
||||
|
||||
let activity = AnnouncableActivities::CollectionRemove(remove);
|
||||
@ -66,28 +65,28 @@ impl CollectionRemove {
|
||||
community: &ApubCommunity,
|
||||
featured_post: &ApubPost,
|
||||
actor: &ApubPerson,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let id = generate_activity_id(
|
||||
RemoveType::Remove,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let remove = CollectionRemove {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
to: vec![public()],
|
||||
object: featured_post.ap_id.clone().into(),
|
||||
target: generate_featured_url(&community.actor_id)?.into(),
|
||||
cc: vec![community.actor_id()],
|
||||
cc: vec![community.id()],
|
||||
kind: RemoveType::Remove,
|
||||
id: id.clone(),
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
};
|
||||
let activity = AnnouncableActivities::CollectionRemove(remove);
|
||||
send_activity_in_community(activity, actor, community, vec![], true, context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for CollectionRemove {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -101,38 +100,23 @@ impl ActivityHandler for CollectionRemove {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
verify_mod_action(
|
||||
&self.actor,
|
||||
self.object.inner(),
|
||||
community.id,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
verify_mod_action(&self.actor, &self.object, community.id, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let (community, collection_type) =
|
||||
Community::get_by_collection_url(context.pool(), &self.target.into()).await?;
|
||||
match collection_type {
|
||||
CollectionType::Moderators => {
|
||||
let remove_mod = self
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
let remove_mod = ObjectId::<ApubPerson>::from(self.object)
|
||||
.dereference(context)
|
||||
.await?;
|
||||
|
||||
let form = CommunityModeratorForm {
|
||||
@ -142,10 +126,7 @@ impl ActivityHandler for CollectionRemove {
|
||||
CommunityModerator::leave(context.pool(), &form).await?;
|
||||
|
||||
// write mod log
|
||||
let actor = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let actor = self.actor.dereference(context).await?;
|
||||
let form = ModAddCommunityForm {
|
||||
mod_person_id: actor.id,
|
||||
other_person_id: remove_mod.id,
|
||||
@ -157,8 +138,8 @@ impl ActivityHandler for CollectionRemove {
|
||||
// TODO: send websocket notification about removed mod
|
||||
}
|
||||
CollectionType::Featured => {
|
||||
let post = ObjectId::<ApubPost>::new(self.object)
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
let post = ObjectId::<ApubPost>::from(self.object)
|
||||
.dereference(context)
|
||||
.await?;
|
||||
let form = PostUpdateForm::builder()
|
||||
.featured_community(Some(false))
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
local_instance,
|
||||
insert_activity,
|
||||
protocol::{
|
||||
activities::{
|
||||
community::lock_page::{LockPage, LockType, UndoLockPage},
|
||||
@ -19,8 +19,11 @@ use crate::{
|
||||
},
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
|
||||
use activitystreams_kinds::{activity::UndoType, public};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
kinds::{activity::UndoType, public},
|
||||
traits::ActivityHandler,
|
||||
};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{LockPost, PostResponse},
|
||||
@ -36,7 +39,7 @@ use lemmy_db_schema::{
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for LockPage {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -49,42 +52,24 @@ impl ActivityHandler for LockPage {
|
||||
self.actor.inner()
|
||||
}
|
||||
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), Self::Error> {
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
check_community_deleted_or_removed(&community)?;
|
||||
verify_mod_action(
|
||||
&self.actor,
|
||||
self.object.inner(),
|
||||
community.id,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
verify_mod_action(&self.actor, self.object.inner(), community.id, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), Self::Error> {
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
let form = PostUpdateForm::builder().locked(Some(true)).build();
|
||||
let post = self
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let post = self.object.dereference(context).await?;
|
||||
Post::update(context.pool(), post.id, &form).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for UndoLockPage {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -97,50 +82,38 @@ impl ActivityHandler for UndoLockPage {
|
||||
self.actor.inner()
|
||||
}
|
||||
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), Self::Error> {
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
check_community_deleted_or_removed(&community)?;
|
||||
verify_mod_action(
|
||||
&self.actor,
|
||||
self.object.object.inner(),
|
||||
community.id,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), Self::Error> {
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let form = PostUpdateForm::builder().locked(Some(false)).build();
|
||||
let post = self
|
||||
.object
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let post = self.object.object.dereference(context).await?;
|
||||
Post::update(context.pool(), post.id, &form).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for LockPost {
|
||||
type Response = PostResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -156,16 +129,16 @@ impl SendActivity for LockPost {
|
||||
LockType::Lock,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let community_id: Url = response.post_view.community.actor_id.clone().into();
|
||||
let actor = ObjectId::new(local_user_view.person.actor_id.clone());
|
||||
let community_id = response.post_view.community.actor_id.clone();
|
||||
let actor = local_user_view.person.actor_id.clone().into();
|
||||
let lock = LockPage {
|
||||
actor,
|
||||
to: vec![public()],
|
||||
object: ObjectId::new(response.post_view.post.ap_id.clone()),
|
||||
cc: vec![community_id.clone()],
|
||||
object: response.post_view.post.ap_id.clone().into(),
|
||||
cc: vec![community_id.clone().into()],
|
||||
kind: LockType::Lock,
|
||||
id,
|
||||
audience: Some(ObjectId::new(community_id)),
|
||||
audience: Some(community_id.into()),
|
||||
};
|
||||
let activity = if request.locked {
|
||||
AnnouncableActivities::LockPost(lock)
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::community::announce::AnnounceActivity,
|
||||
};
|
||||
use activitypub_federation::traits::Actor;
|
||||
use activitypub_federation::{config::Data, traits::Actor};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::source::person::PersonFollower;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -36,29 +36,30 @@ pub(crate) async fn send_activity_in_community(
|
||||
community: &ApubCommunity,
|
||||
extra_inboxes: Vec<Url>,
|
||||
is_mod_action: bool,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
// send to extra_inboxes
|
||||
send_lemmy_activity(context, activity.clone(), actor, extra_inboxes, false).await?;
|
||||
// send to any users which are mentioned or affected directly
|
||||
let mut inboxes = extra_inboxes;
|
||||
|
||||
// send to user followers
|
||||
if !is_mod_action {
|
||||
inboxes.append(
|
||||
&mut PersonFollower::list_followers(context.pool(), actor.id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| ApubPerson(p).shared_inbox_or_inbox())
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
if community.local {
|
||||
// send directly to community followers
|
||||
AnnounceActivity::send(activity.clone().try_into()?, community, context).await?;
|
||||
} else {
|
||||
// send to the community, which will then forward to followers
|
||||
let inbox = vec![community.shared_inbox_or_inbox()];
|
||||
send_lemmy_activity(context, activity.clone(), actor, inbox, false).await?;
|
||||
}
|
||||
|
||||
// send to those who follow `actor`
|
||||
if !is_mod_action {
|
||||
let inboxes = PersonFollower::list_followers(context.pool(), actor.id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| ApubPerson(p).shared_inbox_or_inbox())
|
||||
.collect();
|
||||
send_lemmy_activity(context, activity, actor, inboxes, false).await?;
|
||||
inboxes.push(community.shared_inbox_or_inbox());
|
||||
}
|
||||
|
||||
send_lemmy_activity(context, activity.clone(), actor, inboxes, false).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,18 +1,17 @@
|
||||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{activities::community::report::Report, InCommunity},
|
||||
ActorType,
|
||||
PostOrComment,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::FlagType,
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use activitystreams_kinds::activity::FlagType;
|
||||
use lemmy_api_common::{
|
||||
comment::{CommentReportResponse, CreateCommentReport},
|
||||
context::LemmyContext,
|
||||
@ -31,21 +30,21 @@ use lemmy_db_views::structs::{CommentReportView, PostReportView};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for CreatePostReport {
|
||||
type Response = PostReportResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
Report::send(
|
||||
ObjectId::new(response.post_report_view.post.ap_id.clone()),
|
||||
ObjectId::from(response.post_report_view.post.ap_id.clone()),
|
||||
&local_user_view.person.into(),
|
||||
ObjectId::new(response.post_report_view.community.actor_id.clone()),
|
||||
ObjectId::from(response.post_report_view.community.actor_id.clone()),
|
||||
request.reason.to_string(),
|
||||
context,
|
||||
)
|
||||
@ -53,21 +52,21 @@ impl SendActivity for CreatePostReport {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for CreateCommentReport {
|
||||
type Response = CommentReportResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
Report::send(
|
||||
ObjectId::new(response.comment_report_view.comment.ap_id.clone()),
|
||||
ObjectId::from(response.comment_report_view.comment.ap_id.clone()),
|
||||
&local_user_view.person.into(),
|
||||
ObjectId::new(response.comment_report_view.community.actor_id.clone()),
|
||||
ObjectId::from(response.comment_report_view.community.actor_id.clone()),
|
||||
request.reason.to_string(),
|
||||
context,
|
||||
)
|
||||
@ -82,7 +81,7 @@ impl Report {
|
||||
actor: &ApubPerson,
|
||||
community_id: ObjectId<ApubCommunity>,
|
||||
reason: String,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = community_id.dereference_local(context).await?;
|
||||
let kind = FlagType::Flag;
|
||||
@ -91,13 +90,13 @@ impl Report {
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let report = Report {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
to: [ObjectId::new(community.actor_id())],
|
||||
actor: actor.id().into(),
|
||||
to: [community.id().into()],
|
||||
object: object_id,
|
||||
summary: reason,
|
||||
kind,
|
||||
id: id.clone(),
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
};
|
||||
|
||||
let inbox = vec![community.shared_inbox_or_inbox()];
|
||||
@ -105,7 +104,7 @@ impl Report {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for Report {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -119,31 +118,17 @@ impl ActivityHandler for Report {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
match self
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?
|
||||
{
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, true, context).await?;
|
||||
let actor = self.actor.dereference(context).await?;
|
||||
match self.object.dereference(context).await? {
|
||||
PostOrComment::Post(post) => {
|
||||
let report_form = PostReportForm {
|
||||
creator_id: actor.id,
|
||||
|
@ -7,17 +7,16 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{activities::community::update::UpdateCommunity, InCommunity},
|
||||
ActorType,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
traits::{ActivityHandler, ApubObject},
|
||||
config::Data,
|
||||
kinds::{activity::UpdateType, public},
|
||||
traits::{ActivityHandler, Actor, Object},
|
||||
};
|
||||
use activitystreams_kinds::{activity::UpdateType, public};
|
||||
use lemmy_api_common::{
|
||||
community::{CommunityResponse, EditCommunity, HideCommunity},
|
||||
context::LemmyContext,
|
||||
@ -28,14 +27,14 @@ use lemmy_db_schema::{source::community::Community, traits::Crud};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for EditCommunity {
|
||||
type Response = CommunityResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -49,20 +48,20 @@ impl UpdateCommunity {
|
||||
pub async fn send(
|
||||
community: ApubCommunity,
|
||||
actor: &ApubPerson,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let id = generate_activity_id(
|
||||
UpdateType::Update,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let update = UpdateCommunity {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
to: vec![public()],
|
||||
object: Box::new(community.clone().into_apub(context).await?),
|
||||
cc: vec![community.actor_id()],
|
||||
object: Box::new(community.clone().into_json(context).await?),
|
||||
cc: vec![community.id()],
|
||||
kind: UpdateType::Update,
|
||||
id: id.clone(),
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
};
|
||||
|
||||
let activity = AnnouncableActivities::UpdateCommunity(update);
|
||||
@ -70,7 +69,7 @@ impl UpdateCommunity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for UpdateCommunity {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -84,39 +83,19 @@ impl ActivityHandler for UpdateCommunity {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
verify_mod_action(
|
||||
&self.actor,
|
||||
self.object.id.inner(),
|
||||
community.id,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
ApubCommunity::verify(
|
||||
&self.object,
|
||||
&community.actor_id.clone().into(),
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
verify_mod_action(&self.actor, self.object.id.inner(), community.id, context).await?;
|
||||
ApubCommunity::verify(&self.object, &community.actor_id.clone().into(), context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = self.community(context, request_counter).await?;
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let community = self.community(context).await?;
|
||||
|
||||
let community_update_form = self.object.into_update_form();
|
||||
|
||||
@ -135,14 +114,14 @@ impl ActivityHandler for UpdateCommunity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for HideCommunity {
|
||||
type Response = CommunityResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
|
@ -8,23 +8,22 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
local_instance,
|
||||
insert_activity,
|
||||
mentions::MentionOrValue,
|
||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{
|
||||
activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
|
||||
InCommunity,
|
||||
},
|
||||
ActorType,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
traits::{ActivityHandler, Actor, ApubObject},
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::public,
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::{ActivityHandler, Actor, Object},
|
||||
};
|
||||
use activitystreams_kinds::public;
|
||||
use lemmy_api_common::{
|
||||
comment::{CommentResponse, CreateComment, EditComment},
|
||||
context::LemmyContext,
|
||||
@ -44,14 +43,14 @@ use lemmy_db_schema::{
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for CreateComment {
|
||||
type Response = CommentResponse;
|
||||
|
||||
async fn send_activity(
|
||||
_request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
CreateOrUpdateNote::send(
|
||||
&response.comment_view.comment,
|
||||
@ -63,14 +62,14 @@ impl SendActivity for CreateComment {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for EditComment {
|
||||
type Response = CommentResponse;
|
||||
|
||||
async fn send_activity(
|
||||
_request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
CreateOrUpdateNote::send(
|
||||
&response.comment_view.comment,
|
||||
@ -88,7 +87,7 @@ impl CreateOrUpdateNote {
|
||||
comment: &Comment,
|
||||
person_id: PersonId,
|
||||
kind: CreateOrUpdateType,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
// TODO: might be helpful to add a comment method to retrieve community directly
|
||||
let post_id = comment.post_id;
|
||||
@ -101,17 +100,17 @@ impl CreateOrUpdateNote {
|
||||
kind.clone(),
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let note = ApubComment(comment.clone()).into_apub(context).await?;
|
||||
let note = ApubComment(comment.clone()).into_json(context).await?;
|
||||
|
||||
let create_or_update = CreateOrUpdateNote {
|
||||
actor: ObjectId::new(person.actor_id()),
|
||||
actor: person.id().into(),
|
||||
to: vec![public()],
|
||||
cc: note.cc.clone(),
|
||||
tag: note.tag.clone(),
|
||||
object: note,
|
||||
kind,
|
||||
id: id.clone(),
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
};
|
||||
|
||||
let tagged_users: Vec<ObjectId<ApubPerson>> = create_or_update
|
||||
@ -125,13 +124,11 @@ impl CreateOrUpdateNote {
|
||||
}
|
||||
})
|
||||
.map(|t| t.href.clone())
|
||||
.map(ObjectId::new)
|
||||
.map(ObjectId::from)
|
||||
.collect();
|
||||
let mut inboxes = vec![];
|
||||
for t in tagged_users {
|
||||
let person = t
|
||||
.dereference(context, local_instance(context).await, &mut 0)
|
||||
.await?;
|
||||
let person = t.dereference(context).await?;
|
||||
inboxes.push(person.shared_inbox_or_inbox());
|
||||
}
|
||||
|
||||
@ -140,7 +137,7 @@ impl CreateOrUpdateNote {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for CreateOrUpdateNote {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -154,47 +151,37 @@ impl ActivityHandler for CreateOrUpdateNote {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let post = self.object.get_parents(context, request_counter).await?.0;
|
||||
let community = self.community(context, request_counter).await?;
|
||||
let post = self.object.get_parents(context).await?.0;
|
||||
let community = self.community(context).await?;
|
||||
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||
check_community_deleted_or_removed(&community)?;
|
||||
check_post_deleted_or_removed(&post)?;
|
||||
|
||||
ApubComment::verify(&self.object, self.actor.inner(), context, request_counter).await?;
|
||||
ApubComment::verify(&self.object, self.actor.inner(), context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
// Need to do this check here instead of Note::from_apub because we need the person who
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
// Need to do this check here instead of Note::from_json because we need the person who
|
||||
// send the activity, not the comment author.
|
||||
let existing_comment = self.object.id.dereference_local(context).await.ok();
|
||||
if let (Some(distinguished), Some(existing_comment)) =
|
||||
(self.object.distinguished, existing_comment)
|
||||
{
|
||||
if distinguished != existing_comment.distinguished {
|
||||
let creator = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let (post, _) = self.object.get_parents(context, request_counter).await?;
|
||||
let creator = self.actor.dereference(context).await?;
|
||||
let (post, _) = self.object.get_parents(context).await?;
|
||||
is_mod_or_admin(context.pool(), creator.id, post.community_id).await?;
|
||||
}
|
||||
}
|
||||
|
||||
let comment = ApubComment::from_apub(self.object, context, request_counter).await?;
|
||||
let comment = ApubComment::from_json(self.object, context).await?;
|
||||
|
||||
// author likes their own comment by default
|
||||
let like_form = CommentLikeForm {
|
||||
@ -206,14 +193,8 @@ impl ActivityHandler for CreateOrUpdateNote {
|
||||
CommentLike::like(context.pool(), &like_form).await?;
|
||||
|
||||
let do_send_email = self.kind == CreateOrUpdateType::Create;
|
||||
let recipients = get_comment_notif_recipients(
|
||||
&self.actor,
|
||||
&comment,
|
||||
do_send_email,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
let recipients =
|
||||
get_comment_notif_recipients(&self.actor, &comment, do_send_email, context).await?;
|
||||
let notif_type = match self.kind {
|
||||
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
|
||||
CreateOrUpdateType::Update => UserOperationCrud::EditComment,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{local_instance, objects::person::ApubPerson};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use crate::objects::person::ApubPerson;
|
||||
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
||||
use lemmy_api_common::{context::LemmyContext, websocket::send::send_local_notifs};
|
||||
use lemmy_db_schema::{
|
||||
newtypes::LocalUserId,
|
||||
@ -17,14 +17,11 @@ async fn get_comment_notif_recipients(
|
||||
actor: &ObjectId<ApubPerson>,
|
||||
comment: &Comment,
|
||||
do_send_email: bool,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<Vec<LocalUserId>, LemmyError> {
|
||||
let post_id = comment.post_id;
|
||||
let post = Post::read(context.pool(), post_id).await?;
|
||||
let actor = actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let actor = actor.dereference(context).await?;
|
||||
|
||||
// Note:
|
||||
// Although mentions could be gotten from the post tags (they are included there), or the ccs,
|
||||
|
@ -8,21 +8,20 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||
protocol::{
|
||||
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
|
||||
InCommunity,
|
||||
},
|
||||
ActorType,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
traits::{ActivityHandler, ApubObject},
|
||||
utils::{verify_domains_match, verify_urls_match},
|
||||
config::Data,
|
||||
kinds::public,
|
||||
protocol::verification::{verify_domains_match, verify_urls_match},
|
||||
traits::{ActivityHandler, Actor, Object},
|
||||
};
|
||||
use activitystreams_kinds::public;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{CreatePost, EditPost, PostResponse},
|
||||
@ -40,14 +39,14 @@ use lemmy_db_schema::{
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for CreatePost {
|
||||
type Response = PostResponse;
|
||||
|
||||
async fn send_activity(
|
||||
_request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
CreateOrUpdatePage::send(
|
||||
&response.post_view.post,
|
||||
@ -59,14 +58,14 @@ impl SendActivity for CreatePost {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for EditPost {
|
||||
type Response = PostResponse;
|
||||
|
||||
async fn send_activity(
|
||||
_request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
CreateOrUpdatePage::send(
|
||||
&response.post_view.post,
|
||||
@ -84,20 +83,20 @@ impl CreateOrUpdatePage {
|
||||
actor: &ApubPerson,
|
||||
community: &ApubCommunity,
|
||||
kind: CreateOrUpdateType,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<CreateOrUpdatePage, LemmyError> {
|
||||
let id = generate_activity_id(
|
||||
kind.clone(),
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
Ok(CreateOrUpdatePage {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
to: vec![public()],
|
||||
object: post.into_apub(context).await?,
|
||||
cc: vec![community.actor_id()],
|
||||
object: post.into_json(context).await?,
|
||||
cc: vec![community.id()],
|
||||
kind,
|
||||
id: id.clone(),
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
})
|
||||
}
|
||||
|
||||
@ -106,7 +105,7 @@ impl CreateOrUpdatePage {
|
||||
post: &Post,
|
||||
person_id: PersonId,
|
||||
kind: CreateOrUpdateType,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let post = ApubPost(post.clone());
|
||||
let community_id = post.community_id;
|
||||
@ -130,7 +129,7 @@ impl CreateOrUpdatePage {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for CreateOrUpdatePage {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -144,14 +143,10 @@ impl ActivityHandler for CreateOrUpdatePage {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
check_community_deleted_or_removed(&community)?;
|
||||
|
||||
match self.kind {
|
||||
@ -173,31 +168,21 @@ impl ActivityHandler for CreateOrUpdatePage {
|
||||
CreateOrUpdateType::Update => {
|
||||
let is_mod_action = self.object.is_mod_action(context).await?;
|
||||
if is_mod_action {
|
||||
verify_mod_action(
|
||||
&self.actor,
|
||||
self.object.id.inner(),
|
||||
community.id,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
verify_mod_action(&self.actor, self.object.id.inner(), community.id, context).await?;
|
||||
} else {
|
||||
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||
verify_urls_match(self.actor.inner(), self.object.creator()?.inner())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ApubPost::verify(&self.object, self.actor.inner(), context, request_counter).await?;
|
||||
ApubPost::verify(&self.object, self.actor.inner(), context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let post = ApubPost::from_apub(self.object, context, request_counter).await?;
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let post = ApubPost::from_json(self.object, context).await?;
|
||||
|
||||
// author likes their own post by default
|
||||
let like_form = PostLikeForm {
|
||||
|
@ -1,18 +1,17 @@
|
||||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_person},
|
||||
insert_activity,
|
||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||
protocol::activities::{
|
||||
create_or_update::chat_message::CreateOrUpdateChatMessage,
|
||||
CreateOrUpdateType,
|
||||
},
|
||||
ActorType,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
traits::{ActivityHandler, Actor, ApubObject},
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::{ActivityHandler, Actor, Object},
|
||||
};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
@ -27,14 +26,14 @@ use lemmy_db_schema::{
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for CreatePrivateMessage {
|
||||
type Response = PrivateMessageResponse;
|
||||
|
||||
async fn send_activity(
|
||||
_request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
CreateOrUpdateChatMessage::send(
|
||||
&response.private_message_view.private_message,
|
||||
@ -45,14 +44,14 @@ impl SendActivity for CreatePrivateMessage {
|
||||
.await
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for EditPrivateMessage {
|
||||
type Response = PrivateMessageResponse;
|
||||
|
||||
async fn send_activity(
|
||||
_request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
CreateOrUpdateChatMessage::send(
|
||||
&response.private_message_view.private_message,
|
||||
@ -70,7 +69,7 @@ impl CreateOrUpdateChatMessage {
|
||||
private_message: &PrivateMessage,
|
||||
sender_id: PersonId,
|
||||
kind: CreateOrUpdateType,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let recipient_id = private_message.recipient_id;
|
||||
let sender: ApubPerson = Person::read(context.pool(), sender_id).await?.into();
|
||||
@ -82,10 +81,10 @@ impl CreateOrUpdateChatMessage {
|
||||
)?;
|
||||
let create_or_update = CreateOrUpdateChatMessage {
|
||||
id: id.clone(),
|
||||
actor: ObjectId::new(sender.actor_id()),
|
||||
to: [ObjectId::new(recipient.actor_id())],
|
||||
actor: sender.id().into(),
|
||||
to: [recipient.id().into()],
|
||||
object: ApubPrivateMessage(private_message.clone())
|
||||
.into_apub(context)
|
||||
.into_json(context)
|
||||
.await?,
|
||||
kind,
|
||||
};
|
||||
@ -94,7 +93,7 @@ impl CreateOrUpdateChatMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for CreateOrUpdateChatMessage {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -108,26 +107,18 @@ impl ActivityHandler for CreateOrUpdateChatMessage {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
verify_person(&self.actor, context).await?;
|
||||
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||
verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?;
|
||||
ApubPrivateMessage::verify(&self.object, self.actor.inner(), context, request_counter).await?;
|
||||
ApubPrivateMessage::verify(&self.object, self.actor.inner(), context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let private_message =
|
||||
ApubPrivateMessage::from_apub(self.object, context, request_counter).await?;
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, true, context).await?;
|
||||
let private_message = ApubPrivateMessage::from_json(self.object, context).await?;
|
||||
|
||||
let notif_type = match self.kind {
|
||||
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
|
||||
|
@ -3,12 +3,11 @@ use crate::{
|
||||
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
||||
generate_activity_id,
|
||||
},
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
insert_activity,
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
|
||||
use activitystreams_kinds::activity::DeleteType;
|
||||
use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::ActivityHandler};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
websocket::{
|
||||
@ -35,7 +34,7 @@ use lemmy_db_schema::{
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for Delete {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -49,21 +48,14 @@ impl ActivityHandler for Delete {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
verify_delete_activity(self, self.summary.is_some(), context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
if let Some(reason) = self.summary {
|
||||
// We set reason to empty string if it doesn't exist, to distinguish between delete and
|
||||
// remove. Here we change it back to option, so we don't write it to db.
|
||||
@ -73,24 +65,14 @@ impl ActivityHandler for Delete {
|
||||
Some(reason)
|
||||
};
|
||||
receive_remove_action(
|
||||
&self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?,
|
||||
&self.actor.dereference(context).await?,
|
||||
self.object.id(),
|
||||
reason,
|
||||
context,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
receive_delete_action(
|
||||
self.object.id(),
|
||||
&self.actor,
|
||||
true,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await
|
||||
receive_delete_action(self.object.id(), &self.actor, true, context).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,7 +84,7 @@ impl Delete {
|
||||
to: Url,
|
||||
community: Option<&Community>,
|
||||
summary: Option<String>,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<Delete, LemmyError> {
|
||||
let id = generate_activity_id(
|
||||
DeleteType::Delete,
|
||||
@ -110,14 +92,14 @@ impl Delete {
|
||||
)?;
|
||||
let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
|
||||
Ok(Delete {
|
||||
actor: ObjectId::new(actor.actor_id.clone()),
|
||||
actor: actor.actor_id.clone().into(),
|
||||
to: vec![to],
|
||||
object: IdOrNestedObject::Id(object.id()),
|
||||
cc: cc.into_iter().collect(),
|
||||
kind: DeleteType::Delete,
|
||||
summary,
|
||||
id,
|
||||
audience: community.map(|c| ObjectId::<ApubCommunity>::new(c.actor_id.clone())),
|
||||
audience: community.map(|c| c.actor_id.clone().into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -127,7 +109,7 @@ pub(in crate::activities) async fn receive_remove_action(
|
||||
actor: &ApubPerson,
|
||||
object: &Url,
|
||||
reason: Option<String>,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
use UserOperationCrud::*;
|
||||
match DeletableObjects::read_from_db(object, context).await? {
|
||||
|
@ -1,17 +1,16 @@
|
||||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
||||
protocol::activities::deletion::delete_user::DeleteUser,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
traits::ActivityHandler,
|
||||
utils::verify_urls_match,
|
||||
config::Data,
|
||||
kinds::{activity::DeleteType, public},
|
||||
protocol::verification::verify_urls_match,
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use activitystreams_kinds::{activity::DeleteType, public};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::{DeleteAccount, DeleteAccountResponse},
|
||||
@ -20,14 +19,14 @@ use lemmy_api_common::{
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for DeleteAccount {
|
||||
type Response = DeleteAccountResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -40,15 +39,14 @@ impl SendActivity for DeleteAccount {
|
||||
)
|
||||
.await?;
|
||||
|
||||
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(),
|
||||
actor: actor.id().into(),
|
||||
to: vec![public()],
|
||||
object: actor_id,
|
||||
object: actor.id().into(),
|
||||
kind: DeleteType::Delete,
|
||||
id: id.clone(),
|
||||
cc: vec![],
|
||||
@ -62,7 +60,7 @@ impl SendActivity for DeleteAccount {
|
||||
|
||||
/// 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)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for DeleteUser {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -75,26 +73,16 @@ impl ActivityHandler for DeleteUser {
|
||||
self.actor.inner()
|
||||
}
|
||||
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
verify_is_public(&self.to, &[])?;
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
verify_person(&self.actor, context).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, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
let actor = self.actor.dereference(context).await?;
|
||||
delete_user_account(
|
||||
actor.id,
|
||||
context.pool(),
|
||||
|
@ -8,7 +8,6 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
local_instance,
|
||||
objects::{
|
||||
comment::ApubComment,
|
||||
community::ApubCommunity,
|
||||
@ -20,15 +19,15 @@ use crate::{
|
||||
activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||
InCommunity,
|
||||
},
|
||||
ActorType,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
traits::{Actor, ApubObject},
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::public,
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::{Actor, Object},
|
||||
};
|
||||
use activitystreams_kinds::public;
|
||||
use lemmy_api_common::{
|
||||
comment::{CommentResponse, DeleteComment, RemoveComment},
|
||||
community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
|
||||
@ -64,14 +63,14 @@ pub mod delete;
|
||||
pub mod delete_user;
|
||||
pub mod undo_delete;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for DeletePost {
|
||||
type Response = PostResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -89,14 +88,14 @@ impl SendActivity for DeletePost {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for RemovePost {
|
||||
type Response = PostResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -114,14 +113,14 @@ impl SendActivity for RemovePost {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for DeleteComment {
|
||||
type Response = CommentResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community_id = response.comment_view.community.id;
|
||||
let community = Community::read(context.pool(), community_id).await?;
|
||||
@ -132,14 +131,14 @@ impl SendActivity for DeleteComment {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for RemoveComment {
|
||||
type Response = CommentResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -158,14 +157,14 @@ impl SendActivity for RemoveComment {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for DeletePrivateMessage {
|
||||
type Response = PrivateMessageResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -179,14 +178,14 @@ impl SendActivity for DeletePrivateMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for DeleteCommunity {
|
||||
type Response = CommunityResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -204,14 +203,14 @@ impl SendActivity for DeleteCommunity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for RemoveCommunity {
|
||||
type Response = CommunityResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
@ -238,7 +237,7 @@ async fn send_apub_delete_in_community(
|
||||
object: DeletableObjects,
|
||||
reason: Option<String>,
|
||||
deleted: bool,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor = ApubPerson::from(actor);
|
||||
let is_mod_action = reason.is_some();
|
||||
@ -265,7 +264,7 @@ async fn send_apub_delete_private_message(
|
||||
actor: &ApubPerson,
|
||||
pm: PrivateMessage,
|
||||
deleted: bool,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let recipient_id = pm.recipient_id;
|
||||
let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into();
|
||||
@ -273,10 +272,10 @@ async fn send_apub_delete_private_message(
|
||||
let deletable = DeletableObjects::PrivateMessage(pm.into());
|
||||
let inbox = vec![recipient.shared_inbox_or_inbox()];
|
||||
if deleted {
|
||||
let delete = Delete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
|
||||
let delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?;
|
||||
send_lemmy_activity(context, delete, actor, inbox, true).await?;
|
||||
} else {
|
||||
let undo = UndoDelete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
|
||||
let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?;
|
||||
send_lemmy_activity(context, undo, actor, inbox, true).await?;
|
||||
};
|
||||
Ok(())
|
||||
@ -293,18 +292,18 @@ impl DeletableObjects {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn read_from_db(
|
||||
ap_id: &Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<DeletableObjects, LemmyError> {
|
||||
if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
|
||||
if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
|
||||
return Ok(DeletableObjects::Community(c));
|
||||
}
|
||||
if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? {
|
||||
if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
|
||||
return Ok(DeletableObjects::Post(p));
|
||||
}
|
||||
if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
|
||||
if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? {
|
||||
return Ok(DeletableObjects::Comment(c));
|
||||
}
|
||||
if let Some(p) = ApubPrivateMessage::read_from_apub_id(ap_id.clone(), context).await? {
|
||||
if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? {
|
||||
return Ok(DeletableObjects::PrivateMessage(p));
|
||||
}
|
||||
Err(diesel::NotFound.into())
|
||||
@ -312,7 +311,7 @@ impl DeletableObjects {
|
||||
|
||||
pub(crate) fn id(&self) -> Url {
|
||||
match self {
|
||||
DeletableObjects::Community(c) => c.actor_id(),
|
||||
DeletableObjects::Community(c) => c.id(),
|
||||
DeletableObjects::Comment(c) => c.ap_id.clone().into(),
|
||||
DeletableObjects::Post(p) => p.ap_id.clone().into(),
|
||||
DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
|
||||
@ -324,8 +323,7 @@ impl DeletableObjects {
|
||||
pub(in crate::activities) async fn verify_delete_activity(
|
||||
activity: &Delete,
|
||||
is_mod_action: bool,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
|
||||
match object {
|
||||
@ -334,27 +332,19 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||
if community.local {
|
||||
// can only do this check for local community, in remote case it would try to fetch the
|
||||
// deleted community (which fails)
|
||||
verify_person_in_community(&activity.actor, &community, context, request_counter).await?;
|
||||
verify_person_in_community(&activity.actor, &community, context).await?;
|
||||
}
|
||||
// community deletion is always a mod (or admin) action
|
||||
verify_mod_action(
|
||||
&activity.actor,
|
||||
activity.object.id(),
|
||||
community.id,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?;
|
||||
}
|
||||
DeletableObjects::Post(p) => {
|
||||
verify_is_public(&activity.to, &[])?;
|
||||
verify_delete_post_or_comment(
|
||||
&activity.actor,
|
||||
&p.ap_id.clone().into(),
|
||||
&activity.community(context, request_counter).await?,
|
||||
&activity.community(context).await?,
|
||||
is_mod_action,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@ -363,15 +353,14 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||
verify_delete_post_or_comment(
|
||||
&activity.actor,
|
||||
&c.ap_id.clone().into(),
|
||||
&activity.community(context, request_counter).await?,
|
||||
&activity.community(context).await?,
|
||||
is_mod_action,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
DeletableObjects::PrivateMessage(_) => {
|
||||
verify_person(&activity.actor, context, request_counter).await?;
|
||||
verify_person(&activity.actor, context).await?;
|
||||
verify_domains_match(activity.actor.inner(), activity.object.id())?;
|
||||
}
|
||||
}
|
||||
@ -384,12 +373,11 @@ async fn verify_delete_post_or_comment(
|
||||
object_id: &Url,
|
||||
community: &ApubCommunity,
|
||||
is_mod_action: bool,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_person_in_community(actor, community, context, request_counter).await?;
|
||||
verify_person_in_community(actor, community, context).await?;
|
||||
if is_mod_action {
|
||||
verify_mod_action(actor, object_id, community.id, context, request_counter).await?;
|
||||
verify_mod_action(actor, object_id, community.id, context).await?;
|
||||
} else {
|
||||
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
|
||||
verify_domains_match(actor.inner(), object_id)?;
|
||||
@ -403,17 +391,12 @@ async fn receive_delete_action(
|
||||
object: &Url,
|
||||
actor: &ObjectId<ApubPerson>,
|
||||
deleted: bool,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
match DeletableObjects::read_from_db(object, context).await? {
|
||||
DeletableObjects::Community(community) => {
|
||||
if community.local {
|
||||
let mod_: Person = actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?
|
||||
.deref()
|
||||
.clone();
|
||||
let mod_: Person = actor.dereference(context).await?.deref().clone();
|
||||
let object = DeletableObjects::Community(community.clone());
|
||||
let c: Community = community.deref().deref().clone();
|
||||
send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
|
||||
|
@ -3,12 +3,11 @@ use crate::{
|
||||
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
||||
generate_activity_id,
|
||||
},
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
insert_activity,
|
||||
objects::person::ApubPerson,
|
||||
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use activitypub_federation::{config::Data, kinds::activity::UndoType, traits::ActivityHandler};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
websocket::{
|
||||
@ -35,7 +34,7 @@ use lemmy_db_schema::{
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for UndoDelete {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -48,48 +47,24 @@ impl ActivityHandler for UndoDelete {
|
||||
self.actor.inner()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
self.object.verify(context, request_counter).await?;
|
||||
verify_delete_activity(
|
||||
&self.object,
|
||||
self.object.summary.is_some(),
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
|
||||
self.object.verify(data).await?;
|
||||
verify_delete_activity(&self.object, self.object.summary.is_some(), data).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, false, context).await?;
|
||||
if self.object.summary.is_some() {
|
||||
UndoDelete::receive_undo_remove_action(
|
||||
&self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?,
|
||||
&self.actor.dereference(context).await?,
|
||||
self.object.object.id(),
|
||||
context,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
receive_delete_action(
|
||||
self.object.object.id(),
|
||||
&self.actor,
|
||||
false,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await
|
||||
receive_delete_action(self.object.object.id(), &self.actor, false, context).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,7 +77,7 @@ impl UndoDelete {
|
||||
to: Url,
|
||||
community: Option<&Community>,
|
||||
summary: Option<String>,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<UndoDelete, LemmyError> {
|
||||
let object = Delete::new(actor, object, to.clone(), community, summary, context)?;
|
||||
|
||||
@ -112,13 +87,13 @@ impl UndoDelete {
|
||||
)?;
|
||||
let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
|
||||
Ok(UndoDelete {
|
||||
actor: ObjectId::new(actor.actor_id.clone()),
|
||||
actor: actor.actor_id.clone().into(),
|
||||
to: vec![to],
|
||||
object,
|
||||
cc: cc.into_iter().collect(),
|
||||
kind: UndoType::Undo,
|
||||
id,
|
||||
audience: community.map(|c| ObjectId::<ApubCommunity>::new(c.actor_id.clone())),
|
||||
audience: community.map(|c| c.actor_id.clone().into()),
|
||||
})
|
||||
}
|
||||
|
||||
@ -126,7 +101,7 @@ impl UndoDelete {
|
||||
pub(in crate::activities) async fn receive_undo_remove_action(
|
||||
actor: &ApubPerson,
|
||||
object: &Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
use UserOperationCrud::*;
|
||||
match DeletableObjects::read_from_db(object, context).await? {
|
||||
|
@ -1,16 +1,14 @@
|
||||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity},
|
||||
local_instance,
|
||||
insert_activity,
|
||||
protocol::activities::following::{accept::AcceptFollow, follow::Follow},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
config::Data,
|
||||
kinds::activity::AcceptType,
|
||||
protocol::verification::verify_urls_match,
|
||||
traits::{ActivityHandler, Actor},
|
||||
utils::verify_urls_match,
|
||||
};
|
||||
use activitystreams_kinds::activity::AcceptType;
|
||||
use lemmy_api_common::{
|
||||
community::CommunityResponse,
|
||||
context::LemmyContext,
|
||||
@ -27,19 +25,11 @@ use url::Url;
|
||||
|
||||
impl AcceptFollow {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send(
|
||||
follow: Follow,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
pub async fn send(follow: Follow, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
let user_or_community = follow.object.dereference_local(context).await?;
|
||||
let person = follow
|
||||
.actor
|
||||
.clone()
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let person = follow.actor.clone().dereference(context).await?;
|
||||
let accept = AcceptFollow {
|
||||
actor: ObjectId::new(user_or_community.actor_id()),
|
||||
actor: user_or_community.id().into(),
|
||||
object: follow,
|
||||
kind: AcceptType::Accept,
|
||||
id: generate_activity_id(
|
||||
@ -53,7 +43,7 @@ impl AcceptFollow {
|
||||
}
|
||||
|
||||
/// Handle accepted follows
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for AcceptFollow {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -67,31 +57,17 @@ impl ActivityHandler for AcceptFollow {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
verify_urls_match(self.actor.inner(), self.object.object.inner())?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
self.object.verify(context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let person = self
|
||||
.object
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, true, context).await?;
|
||||
let community = self.actor.dereference(context).await?;
|
||||
let person = self.object.actor.dereference(context).await?;
|
||||
// This will throw an error if no follow was requested
|
||||
let community_id = community.id;
|
||||
let person_id = person.id;
|
||||
|
@ -6,22 +6,20 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
},
|
||||
fetcher::user_or_community::UserOrCommunity,
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::following::{
|
||||
accept::AcceptFollow,
|
||||
follow::Follow,
|
||||
undo_follow::UndoFollow,
|
||||
},
|
||||
ActorType,
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
config::Data,
|
||||
kinds::activity::FollowType,
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use activitystreams_kinds::activity::FollowType;
|
||||
use lemmy_api_common::{
|
||||
community::{BlockCommunity, BlockCommunityResponse},
|
||||
context::LemmyContext,
|
||||
@ -41,11 +39,11 @@ impl Follow {
|
||||
pub(in crate::activities::following) fn new(
|
||||
actor: &ApubPerson,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<Follow, LemmyError> {
|
||||
Ok(Follow {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
object: ObjectId::new(community.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
object: community.id().into(),
|
||||
kind: FollowType::Follow,
|
||||
id: generate_activity_id(
|
||||
FollowType::Follow,
|
||||
@ -58,7 +56,7 @@ impl Follow {
|
||||
pub async fn send(
|
||||
actor: &ApubPerson,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community_follower_form = CommunityFollowerForm {
|
||||
community_id: community.id,
|
||||
@ -75,7 +73,7 @@ impl Follow {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for Follow {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -89,36 +87,20 @@ impl ActivityHandler for Follow {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
let object = self
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
verify_person(&self.actor, context).await?;
|
||||
let object = self.object.dereference(context).await?;
|
||||
if let UserOrCommunity::Community(c) = object {
|
||||
verify_person_in_community(&self.actor, &c, context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &c, context).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let object = self
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, true, context).await?;
|
||||
let actor = self.actor.dereference(context).await?;
|
||||
let object = self.object.dereference(context).await?;
|
||||
match object {
|
||||
UserOrCommunity::User(u) => {
|
||||
let form = PersonFollowerForm {
|
||||
@ -138,18 +120,18 @@ impl ActivityHandler for Follow {
|
||||
}
|
||||
}
|
||||
|
||||
AcceptFollow::send(self, context, request_counter).await
|
||||
AcceptFollow::send(self, context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for BlockCommunity {
|
||||
type Response = BlockCommunityResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
|
@ -3,6 +3,7 @@ use crate::{
|
||||
protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::config::Data;
|
||||
use lemmy_api_common::{
|
||||
community::{CommunityResponse, FollowCommunity},
|
||||
context::LemmyContext,
|
||||
@ -15,14 +16,14 @@ pub mod accept;
|
||||
pub mod follow;
|
||||
pub mod undo_follow;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for FollowCommunity {
|
||||
type Response = CommunityResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
_response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
|
||||
|
@ -1,18 +1,16 @@
|
||||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_person},
|
||||
fetcher::user_or_community::UserOrCommunity,
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
config::Data,
|
||||
kinds::activity::UndoType,
|
||||
protocol::verification::verify_urls_match,
|
||||
traits::{ActivityHandler, Actor},
|
||||
utils::verify_urls_match,
|
||||
};
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
@ -29,11 +27,11 @@ impl UndoFollow {
|
||||
pub async fn send(
|
||||
actor: &ApubPerson,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let object = Follow::new(actor, community, context)?;
|
||||
let undo = UndoFollow {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
object,
|
||||
kind: UndoType::Undo,
|
||||
id: generate_activity_id(
|
||||
@ -46,7 +44,7 @@ impl UndoFollow {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for UndoFollow {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -60,32 +58,18 @@ impl ActivityHandler for UndoFollow {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
verify_person(&self.actor, context).await?;
|
||||
self.object.verify(context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let person = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let object = self
|
||||
.object
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, true, context).await?;
|
||||
let person = self.actor.dereference(context).await?;
|
||||
let object = self.object.object.dereference(context).await?;
|
||||
|
||||
match object {
|
||||
UserOrCommunity::User(u) => {
|
||||
|
@ -1,22 +1,19 @@
|
||||
use crate::{
|
||||
insert_activity,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
ActorType,
|
||||
CONTEXT,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::{activity_queue::send_activity, object_id::ObjectId},
|
||||
deser::context::WithContext,
|
||||
activity_queue::send_activity,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::public,
|
||||
protocol::context::WithContext,
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use activitystreams_kinds::public;
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::CommunityId,
|
||||
source::{community::Community, local_site::LocalSite},
|
||||
};
|
||||
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community};
|
||||
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::Serialize;
|
||||
@ -38,12 +35,9 @@ pub mod voting;
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify_person(
|
||||
person_id: &ObjectId<ApubPerson>,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let person = person_id
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let person = person_id.dereference(context).await?;
|
||||
if person.banned {
|
||||
let err = anyhow!("Person {} is banned", person_id);
|
||||
return Err(LemmyError::from_error_message(err, "banned"));
|
||||
@ -57,12 +51,9 @@ async fn verify_person(
|
||||
pub(crate) async fn verify_person_in_community(
|
||||
person_id: &ObjectId<ApubPerson>,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let person = person_id
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let person = person_id.dereference(context).await?;
|
||||
if person.banned {
|
||||
return Err(LemmyError::from_message("Person is banned from site"));
|
||||
}
|
||||
@ -88,12 +79,9 @@ pub(crate) async fn verify_mod_action(
|
||||
mod_id: &ObjectId<ApubPerson>,
|
||||
object_id: &Url,
|
||||
community_id: CommunityId,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let mod_ = mod_id
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let mod_ = mod_id.dereference(context).await?;
|
||||
|
||||
let is_mod_or_admin =
|
||||
CommunityView::is_mod_or_admin(context.pool(), mod_.id, community_id).await?;
|
||||
@ -159,39 +147,22 @@ where
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn send_lemmy_activity<Activity, ActorT>(
|
||||
context: &LemmyContext,
|
||||
data: &Data<LemmyContext>,
|
||||
activity: Activity,
|
||||
actor: &ActorT,
|
||||
inbox: Vec<Url>,
|
||||
sensitive: bool,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
Activity: ActivityHandler + Serialize,
|
||||
ActorT: Actor + ActorType,
|
||||
Activity: ActivityHandler + Serialize + Send + Sync + Clone,
|
||||
ActorT: Actor,
|
||||
Activity: ActivityHandler<Error = LemmyError>,
|
||||
{
|
||||
let federation_enabled = LocalSite::read(context.pool())
|
||||
.await
|
||||
.map(|l| l.federation_enabled)
|
||||
.unwrap_or(false);
|
||||
if !federation_enabled {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Sending activity {}", activity.id().to_string());
|
||||
let activity = WithContext::new(activity, CONTEXT.deref().clone());
|
||||
|
||||
let object_value = serde_json::to_value(&activity)?;
|
||||
insert_activity(activity.id(), object_value, true, sensitive, context.pool()).await?;
|
||||
|
||||
send_activity(
|
||||
activity,
|
||||
actor.get_public_key(),
|
||||
actor.private_key().expect("actor has private key"),
|
||||
inbox,
|
||||
local_instance(context).await,
|
||||
)
|
||||
.await?;
|
||||
insert_activity(activity.id(), &activity, true, sensitive, data).await?;
|
||||
send_activity(activity, actor, inbox, data).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
},
|
||||
SendActivity,
|
||||
};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
||||
use lemmy_api_common::{
|
||||
comment::{CommentResponse, CreateCommentLike},
|
||||
context::LemmyContext,
|
||||
@ -36,16 +36,16 @@ use lemmy_utils::error::LemmyError;
|
||||
pub mod undo_vote;
|
||||
pub mod vote;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for CreatePostLike {
|
||||
type Response = PostResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let object_id = ObjectId::new(response.post_view.post.ap_id.clone());
|
||||
let object_id = ObjectId::from(response.post_view.post.ap_id.clone());
|
||||
let community_id = response.post_view.community.id;
|
||||
send_activity(
|
||||
object_id,
|
||||
@ -58,16 +58,16 @@ impl SendActivity for CreatePostLike {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl SendActivity for CreateCommentLike {
|
||||
type Response = CommentResponse;
|
||||
|
||||
async fn send_activity(
|
||||
request: &Self,
|
||||
response: &Self::Response,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let object_id = ObjectId::new(response.comment_view.comment.ap_id.clone());
|
||||
let object_id = ObjectId::from(response.comment_view.comment.ap_id.clone());
|
||||
let community_id = response.comment_view.community.id;
|
||||
send_activity(
|
||||
object_id,
|
||||
@ -85,7 +85,7 @@ async fn send_activity(
|
||||
community_id: CommunityId,
|
||||
score: i16,
|
||||
jwt: &Sensitive<String>,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = Community::read(context.pool(), community_id).await?.into();
|
||||
let local_user_view = get_local_user_view_from_jwt(jwt, context.pool(), context.secret()).await?;
|
||||
@ -112,7 +112,7 @@ async fn vote_comment(
|
||||
vote_type: &VoteType,
|
||||
actor: ApubPerson,
|
||||
comment: &ApubComment,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let comment_id = comment.id;
|
||||
let like_form = CommentLikeForm {
|
||||
@ -134,7 +134,7 @@ async fn vote_post(
|
||||
vote_type: &VoteType,
|
||||
actor: ApubPerson,
|
||||
post: &ApubPost,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let post_id = post.id;
|
||||
let like_form = PostLikeForm {
|
||||
@ -154,7 +154,7 @@ async fn vote_post(
|
||||
async fn undo_vote_comment(
|
||||
actor: ApubPerson,
|
||||
comment: &ApubComment,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let comment_id = comment.id;
|
||||
let person_id = actor.id;
|
||||
@ -168,7 +168,7 @@ async fn undo_vote_comment(
|
||||
async fn undo_vote_post(
|
||||
actor: ApubPerson,
|
||||
post: &ApubPost,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let post_id = post.id;
|
||||
let person_id = actor.id;
|
||||
|
@ -4,22 +4,20 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
voting::{undo_vote_comment, undo_vote_post},
|
||||
},
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{
|
||||
activities::voting::{undo_vote::UndoVote, vote::Vote},
|
||||
InCommunity,
|
||||
},
|
||||
ActorType,
|
||||
PostOrComment,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
traits::ActivityHandler,
|
||||
utils::verify_urls_match,
|
||||
config::Data,
|
||||
kinds::activity::UndoType,
|
||||
protocol::verification::verify_urls_match,
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
@ -29,22 +27,22 @@ impl UndoVote {
|
||||
vote: Vote,
|
||||
actor: &ApubPerson,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<Self, LemmyError> {
|
||||
Ok(UndoVote {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
object: vote,
|
||||
kind: UndoType::Undo,
|
||||
id: generate_activity_id(
|
||||
UndoType::Undo,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?,
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for UndoVote {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -58,33 +56,19 @@ impl ActivityHandler for UndoVote {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
self.object.verify(context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let object = self
|
||||
.object
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, true, context).await?;
|
||||
let actor = self.actor.dereference(context).await?;
|
||||
let object = self.object.object.dereference(context).await?;
|
||||
match object {
|
||||
PostOrComment::Post(p) => undo_vote_post(actor, &p, context).await,
|
||||
PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await,
|
||||
|
@ -4,16 +4,19 @@ use crate::{
|
||||
verify_person_in_community,
|
||||
voting::{vote_comment, vote_post},
|
||||
},
|
||||
local_instance,
|
||||
insert_activity,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{
|
||||
activities::voting::vote::{Vote, VoteType},
|
||||
InCommunity,
|
||||
},
|
||||
ActorType,
|
||||
PostOrComment,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
@ -26,19 +29,19 @@ impl Vote {
|
||||
actor: &ApubPerson,
|
||||
community: &ApubCommunity,
|
||||
kind: VoteType,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<Vote, LemmyError> {
|
||||
Ok(Vote {
|
||||
actor: ObjectId::new(actor.actor_id()),
|
||||
actor: actor.id().into(),
|
||||
object: object_id,
|
||||
kind: kind.clone(),
|
||||
id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
|
||||
audience: Some(ObjectId::new(community.actor_id())),
|
||||
audience: Some(community.id().into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for Vote {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -52,13 +55,9 @@ impl ActivityHandler for Vote {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = self.community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
let community = self.community(context).await?;
|
||||
verify_person_in_community(&self.actor, &community, context).await?;
|
||||
let enable_downvotes = LocalSite::read(context.pool())
|
||||
.await
|
||||
.map(|l| l.enable_downvotes)
|
||||
@ -70,19 +69,10 @@ impl ActivityHandler for Vote {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor = self
|
||||
.actor
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let object = self
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||
insert_activity(&self.id, &self, false, true, context).await?;
|
||||
let actor = self.actor.dereference(context).await?;
|
||||
let object = self.object.dereference(context).await?;
|
||||
match object {
|
||||
PostOrComment::Post(p) => vote_post(&self.kind, actor, &p, context).await,
|
||||
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, &c, context).await,
|
||||
|
@ -24,7 +24,11 @@ use crate::{
|
||||
InCommunity,
|
||||
},
|
||||
};
|
||||
use activitypub_federation::{data::Data, deser::context::WithContext, traits::ActivityHandler};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
protocol::context::WithContext,
|
||||
traits::ActivityHandler,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -104,29 +108,25 @@ pub enum SiteInboxActivities {
|
||||
DeleteUser(DeleteUser),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for AnnouncableActivities {
|
||||
#[tracing::instrument(skip(self, context))]
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
use AnnouncableActivities::*;
|
||||
match self {
|
||||
CreateOrUpdateComment(a) => a.community(context, request_counter).await,
|
||||
CreateOrUpdatePost(a) => a.community(context, request_counter).await,
|
||||
Vote(a) => a.community(context, request_counter).await,
|
||||
UndoVote(a) => a.community(context, request_counter).await,
|
||||
Delete(a) => a.community(context, request_counter).await,
|
||||
UndoDelete(a) => a.community(context, request_counter).await,
|
||||
UpdateCommunity(a) => a.community(context, request_counter).await,
|
||||
BlockUser(a) => a.community(context, request_counter).await,
|
||||
UndoBlockUser(a) => a.community(context, request_counter).await,
|
||||
CollectionAdd(a) => a.community(context, request_counter).await,
|
||||
CollectionRemove(a) => a.community(context, request_counter).await,
|
||||
LockPost(a) => a.community(context, request_counter).await,
|
||||
UndoLockPost(a) => a.community(context, request_counter).await,
|
||||
CreateOrUpdateComment(a) => a.community(context).await,
|
||||
CreateOrUpdatePost(a) => a.community(context).await,
|
||||
Vote(a) => a.community(context).await,
|
||||
UndoVote(a) => a.community(context).await,
|
||||
Delete(a) => a.community(context).await,
|
||||
UndoDelete(a) => a.community(context).await,
|
||||
UpdateCommunity(a) => a.community(context).await,
|
||||
BlockUser(a) => a.community(context).await,
|
||||
UndoBlockUser(a) => a.community(context).await,
|
||||
CollectionAdd(a) => a.community(context).await,
|
||||
CollectionRemove(a) => a.community(context).await,
|
||||
LockPost(a) => a.community(context).await,
|
||||
UndoLockPost(a) => a.community(context).await,
|
||||
Page(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
fetcher::resolve_actor_identifier,
|
||||
objects::community::ApubCommunity,
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use activitypub_federation::config::Data;
|
||||
use lemmy_api_common::{
|
||||
comment::{GetComments, GetCommentsResponse},
|
||||
context::LemmyContext,
|
||||
@ -20,7 +20,7 @@ use lemmy_db_schema::{
|
||||
use lemmy_db_views::comment_view::CommentQuery;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl PerformApub for GetComments {
|
||||
type Response = GetCommentsResponse;
|
||||
|
||||
@ -41,10 +41,10 @@ impl PerformApub for GetComments {
|
||||
let listing_type = listing_type_with_site_default(data.type_, &local_site)?;
|
||||
|
||||
let community_actor_id = if let Some(name) = &data.community_name {
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, context, true)
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, context, &None, true)
|
||||
.await
|
||||
.ok()
|
||||
.map(|c| c.actor_id)
|
||||
.map(|c| c.actor_id.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
fetcher::resolve_actor_identifier,
|
||||
objects::community::ApubCommunity,
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use activitypub_federation::config::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{GetPosts, GetPostsResponse},
|
||||
@ -18,7 +18,7 @@ use lemmy_db_schema::source::{community::Community, local_site::LocalSite};
|
||||
use lemmy_db_views::post_view::PostQuery;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl PerformApub for GetPosts {
|
||||
type Response = GetPostsResponse;
|
||||
|
||||
@ -43,10 +43,10 @@ impl PerformApub for GetPosts {
|
||||
let limit = data.limit;
|
||||
let community_id = data.community_id;
|
||||
let community_actor_id = if let Some(name) = &data.community_name {
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, context, true)
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, context, &None, true)
|
||||
.await
|
||||
.ok()
|
||||
.map(|c| c.actor_id)
|
||||
.map(|c| c.actor_id.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
use actix_web::web::Data;
|
||||
use activitypub_federation::config::Data;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
@ -9,7 +9,7 @@ mod read_person;
|
||||
mod resolve_object;
|
||||
mod search;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
pub trait PerformApub {
|
||||
type Response: serde::ser::Serialize + Send;
|
||||
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
fetcher::resolve_actor_identifier,
|
||||
objects::community::ApubCommunity,
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use activitypub_federation::config::Data;
|
||||
use lemmy_api_common::{
|
||||
community::{GetCommunity, GetCommunityResponse},
|
||||
context::LemmyContext,
|
||||
@ -21,7 +21,7 @@ use lemmy_db_schema::{
|
||||
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl PerformApub for GetCommunity {
|
||||
type Response = GetCommunityResponse;
|
||||
|
||||
@ -49,7 +49,7 @@ impl PerformApub for GetCommunity {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
let name = data.name.clone().unwrap_or_else(|| "main".to_string());
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(&name, context, true)
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(&name, context, &local_user_view, true)
|
||||
.await
|
||||
.map_err(|e| e.with_message("couldnt_find_community"))?
|
||||
.id
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{api::PerformApub, fetcher::resolve_actor_identifier, objects::person::ApubPerson};
|
||||
use actix_web::web::Data;
|
||||
use activitypub_federation::config::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::{GetPersonDetails, GetPersonDetailsResponse},
|
||||
@ -13,7 +13,7 @@ use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery};
|
||||
use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl PerformApub for GetPersonDetails {
|
||||
type Response = GetPersonDetailsResponse;
|
||||
|
||||
@ -42,7 +42,7 @@ impl PerformApub for GetPersonDetails {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
if let Some(username) = &data.username {
|
||||
resolve_actor_identifier::<ApubPerson, Person>(username, context, true)
|
||||
resolve_actor_identifier::<ApubPerson, Person>(username, context, &local_user_view, true)
|
||||
.await
|
||||
.map_err(|e| e.with_message("couldnt_find_that_username_or_email"))?
|
||||
.id
|
||||
|
@ -2,19 +2,19 @@ use crate::{
|
||||
api::PerformApub,
|
||||
fetcher::search::{search_query_to_object_id, SearchableObjects},
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use activitypub_federation::config::Data;
|
||||
use diesel::NotFound;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::{ResolveObject, ResolveObjectResponse},
|
||||
utils::{check_private_instance, get_local_user_view_from_jwt_opt},
|
||||
utils::{check_private_instance, get_local_user_view_from_jwt},
|
||||
};
|
||||
use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool};
|
||||
use lemmy_db_views::structs::{CommentView, PostView};
|
||||
use lemmy_db_views_actor::structs::{CommunityView, PersonView};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl PerformApub for ResolveObject {
|
||||
type Response = ResolveObjectResponse;
|
||||
|
||||
@ -25,17 +25,15 @@ impl PerformApub for ResolveObject {
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<ResolveObjectResponse, LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = LocalSite::read(context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
let person_id = local_user_view.person.id;
|
||||
check_private_instance(&Some(local_user_view), &local_site)?;
|
||||
|
||||
// In release builds only allow for authenticated users to fetch remote objects
|
||||
let local_only = local_user_view.is_none() && cfg!(not(debug_assertions));
|
||||
let res = search_query_to_object_id(&self.q, local_only, context)
|
||||
let res = search_query_to_object_id(&self.q, context)
|
||||
.await
|
||||
.map_err(|e| e.with_message("couldnt_find_object"))?;
|
||||
convert_response(res, local_user_view.map(|l| l.person.id), context.pool())
|
||||
convert_response(res, person_id, context.pool())
|
||||
.await
|
||||
.map_err(|e| e.with_message("couldnt_find_object"))
|
||||
}
|
||||
@ -43,7 +41,7 @@ impl PerformApub for ResolveObject {
|
||||
|
||||
async fn convert_response(
|
||||
object: SearchableObjects,
|
||||
user_id: Option<PersonId>,
|
||||
user_id: PersonId,
|
||||
pool: &DbPool,
|
||||
) -> Result<ResolveObjectResponse, LemmyError> {
|
||||
use SearchableObjects::*;
|
||||
@ -56,15 +54,15 @@ async fn convert_response(
|
||||
}
|
||||
Community(c) => {
|
||||
removed_or_deleted = c.deleted || c.removed;
|
||||
res.community = Some(CommunityView::read(pool, c.id, user_id, None).await?)
|
||||
res.community = Some(CommunityView::read(pool, c.id, Some(user_id), None).await?)
|
||||
}
|
||||
Post(p) => {
|
||||
removed_or_deleted = p.deleted || p.removed;
|
||||
res.post = Some(PostView::read(pool, p.id, user_id, None).await?)
|
||||
res.post = Some(PostView::read(pool, p.id, Some(user_id), None).await?)
|
||||
}
|
||||
Comment(c) => {
|
||||
removed_or_deleted = c.deleted || c.removed;
|
||||
res.comment = Some(CommentView::read(pool, c.id, user_id).await?)
|
||||
res.comment = Some(CommentView::read(pool, c.id, Some(user_id)).await?)
|
||||
}
|
||||
};
|
||||
// if the object was deleted from database, dont return it
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
fetcher::resolve_actor_identifier,
|
||||
objects::community::ApubCommunity,
|
||||
};
|
||||
use actix_web::web::Data;
|
||||
use activitypub_federation::config::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::{Search, SearchResponse},
|
||||
@ -18,7 +18,7 @@ use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery};
|
||||
use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl PerformApub for Search {
|
||||
type Response = SearchResponse;
|
||||
|
||||
@ -39,8 +39,6 @@ impl PerformApub for Search {
|
||||
|
||||
let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
|
||||
|
||||
let local_user = local_user_view.map(|l| l.local_user);
|
||||
|
||||
let mut posts = Vec::new();
|
||||
let mut comments = Vec::new();
|
||||
let mut communities = Vec::new();
|
||||
@ -56,14 +54,15 @@ impl PerformApub for Search {
|
||||
let search_type = data.type_.unwrap_or(SearchType::All);
|
||||
let community_id = data.community_id;
|
||||
let community_actor_id = if let Some(name) = &data.community_name {
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, context, false)
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, context, &local_user_view, false)
|
||||
.await
|
||||
.ok()
|
||||
.map(|c| c.actor_id)
|
||||
.map(|c| c.actor_id.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let creator_id = data.creator_id;
|
||||
let local_user = local_user_view.map(|l| l.local_user);
|
||||
match search_type {
|
||||
SearchType::Posts => {
|
||||
posts = PostQuery::builder()
|
||||
|
@ -1,16 +1,15 @@
|
||||
use crate::{
|
||||
collections::CommunityContext,
|
||||
objects::post::ApubPost,
|
||||
objects::{community::ApubCommunity, post::ApubPost},
|
||||
protocol::collections::group_featured::GroupFeatured,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
data::Data,
|
||||
traits::{ActivityHandler, ApubObject},
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
kinds::collection::OrderedCollectionType,
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::{ActivityHandler, Collection, Object},
|
||||
};
|
||||
use activitystreams_kinds::collection::OrderedCollectionType;
|
||||
use futures::future::{join_all, try_join_all};
|
||||
use lemmy_api_common::utils::generate_featured_url;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::generate_featured_url};
|
||||
use lemmy_db_schema::{source::post::Post, utils::FETCH_LIMIT_MAX};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
@ -18,58 +17,46 @@ use url::Url;
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ApubCommunityFeatured(Vec<ApubPost>);
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubCommunityFeatured {
|
||||
type DataType = CommunityContext;
|
||||
type ApubType = GroupFeatured;
|
||||
type DbType = ();
|
||||
#[async_trait::async_trait]
|
||||
impl Collection for ApubCommunityFeatured {
|
||||
type Owner = ApubCommunity;
|
||||
type DataType = LemmyContext;
|
||||
type Kind = GroupFeatured;
|
||||
type Error = LemmyError;
|
||||
|
||||
async fn read_from_apub_id(
|
||||
_object_id: Url,
|
||||
data: &Self::DataType,
|
||||
) -> Result<Option<Self>, Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// Only read from database if its a local community, otherwise fetch over http
|
||||
if data.0.local {
|
||||
let community_id = data.0.id;
|
||||
let post_list: Vec<ApubPost> = Post::list_featured_for_community(data.1.pool(), community_id)
|
||||
async fn read_local(
|
||||
owner: &Self::Owner,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Self::Kind, Self::Error> {
|
||||
let ordered_items = try_join_all(
|
||||
Post::list_featured_for_community(data.pool(), owner.id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
Ok(Some(ApubCommunityFeatured(post_list)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, Self::Error> {
|
||||
let ordered_items = try_join_all(self.0.into_iter().map(|p| p.into_apub(&data.1))).await?;
|
||||
.map(ApubPost::from)
|
||||
.map(|p| p.into_json(data)),
|
||||
)
|
||||
.await?;
|
||||
Ok(GroupFeatured {
|
||||
r#type: OrderedCollectionType::OrderedCollection,
|
||||
id: generate_featured_url(&data.0.actor_id)?.into(),
|
||||
id: generate_featured_url(&owner.actor_id)?.into(),
|
||||
total_items: ordered_items.len() as i32,
|
||||
ordered_items,
|
||||
})
|
||||
}
|
||||
|
||||
async fn verify(
|
||||
apub: &Self::ApubType,
|
||||
apub: &Self::Kind,
|
||||
expected_domain: &Url,
|
||||
_data: &Self::DataType,
|
||||
_request_counter: &mut i32,
|
||||
_data: &Data<Self::DataType>,
|
||||
) -> Result<(), Self::Error> {
|
||||
verify_domains_match(expected_domain, &apub.id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn from_apub(
|
||||
apub: Self::ApubType,
|
||||
data: &Self::DataType,
|
||||
_request_counter: &mut i32,
|
||||
async fn from_json(
|
||||
apub: Self::Kind,
|
||||
_owner: &Self::Owner,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Self, Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -85,16 +72,14 @@ impl ApubObject for ApubCommunityFeatured {
|
||||
// We intentionally ignore errors here. This is because the outbox might contain posts from old
|
||||
// Lemmy versions, or from other software which we cant parse. In that case, we simply skip the
|
||||
// item and only parse the ones that work.
|
||||
let data = Data::new(data.1.clone());
|
||||
// process items in parallel, to avoid long delay from fetch_site_metadata() and other processing
|
||||
join_all(posts.into_iter().map(|post| {
|
||||
async {
|
||||
// use separate request counter for each item, otherwise there will be problems with
|
||||
// parallel processing
|
||||
let request_counter = &mut 0;
|
||||
let verify = post.verify(&data, request_counter).await;
|
||||
let verify = post.verify(data).await;
|
||||
if verify.is_ok() {
|
||||
post.receive(&data, request_counter).await.ok();
|
||||
post.receive(data).await.ok();
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
@ -1,17 +1,15 @@
|
||||
use crate::{
|
||||
collections::CommunityContext,
|
||||
local_instance,
|
||||
objects::person::ApubPerson,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::collections::group_moderators::GroupModerators,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
traits::ApubObject,
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::collection::OrderedCollectionType,
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::Collection,
|
||||
};
|
||||
use activitystreams_kinds::collection::OrderedCollectionType;
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::utils::generate_moderators_url;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::generate_moderators_url};
|
||||
use lemmy_db_schema::{
|
||||
source::community::{CommunityModerator, CommunityModeratorForm},
|
||||
traits::Joinable,
|
||||
@ -23,46 +21,26 @@ use url::Url;
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ApubCommunityModerators(pub(crate) Vec<CommunityModeratorView>);
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubCommunityModerators {
|
||||
type DataType = CommunityContext;
|
||||
type ApubType = GroupModerators;
|
||||
#[async_trait::async_trait]
|
||||
impl Collection for ApubCommunityModerators {
|
||||
type Owner = ApubCommunity;
|
||||
type DataType = LemmyContext;
|
||||
type Kind = GroupModerators;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
None
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
_object_id: Url,
|
||||
data: &Self::DataType,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
// Only read from database if its a local community, otherwise fetch over http
|
||||
if data.0.local {
|
||||
let cid = data.0.id;
|
||||
let moderators = CommunityModeratorView::for_community(data.1.pool(), cid).await?;
|
||||
Ok(Some(ApubCommunityModerators(moderators)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||
let ordered_items = self
|
||||
.0
|
||||
async fn read_local(
|
||||
owner: &Self::Owner,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Self::Kind, LemmyError> {
|
||||
let moderators = CommunityModeratorView::for_community(data.pool(), owner.id).await?;
|
||||
let ordered_items = moderators
|
||||
.into_iter()
|
||||
.map(|m| ObjectId::<ApubPerson>::new(m.moderator.actor_id))
|
||||
.map(|m| ObjectId::<ApubPerson>::from(m.moderator.actor_id))
|
||||
.collect();
|
||||
Ok(GroupModerators {
|
||||
r#type: OrderedCollectionType::OrderedCollection,
|
||||
id: generate_moderators_url(&data.0.actor_id)?.into(),
|
||||
id: generate_moderators_url(&owner.actor_id)?.into(),
|
||||
ordered_items,
|
||||
})
|
||||
}
|
||||
@ -71,40 +49,36 @@ impl ApubObject for ApubCommunityModerators {
|
||||
async fn verify(
|
||||
group_moderators: &GroupModerators,
|
||||
expected_domain: &Url,
|
||||
_context: &CommunityContext,
|
||||
_request_counter: &mut i32,
|
||||
_data: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_domains_match(&group_moderators.id, expected_domain)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
apub: Self::ApubType,
|
||||
data: &Self::DataType,
|
||||
request_counter: &mut i32,
|
||||
async fn from_json(
|
||||
apub: Self::Kind,
|
||||
owner: &Self::Owner,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Self, LemmyError> {
|
||||
let community_id = data.0.id;
|
||||
let community_id = owner.id;
|
||||
let current_moderators =
|
||||
CommunityModeratorView::for_community(data.1.pool(), community_id).await?;
|
||||
CommunityModeratorView::for_community(data.pool(), community_id).await?;
|
||||
// Remove old mods from database which arent in the moderators collection anymore
|
||||
for mod_user in ¤t_moderators {
|
||||
let mod_id = ObjectId::new(mod_user.moderator.actor_id.clone());
|
||||
let mod_id = ObjectId::from(mod_user.moderator.actor_id.clone());
|
||||
if !apub.ordered_items.contains(&mod_id) {
|
||||
let community_moderator_form = CommunityModeratorForm {
|
||||
community_id: mod_user.community.id,
|
||||
person_id: mod_user.moderator.id,
|
||||
};
|
||||
CommunityModerator::leave(data.1.pool(), &community_moderator_form).await?;
|
||||
CommunityModerator::leave(data.pool(), &community_moderator_form).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new mods to database which have been added to moderators collection
|
||||
for mod_id in apub.ordered_items {
|
||||
let mod_id = ObjectId::new(mod_id);
|
||||
let mod_user: ApubPerson = mod_id
|
||||
.dereference(&data.1, local_instance(&data.1).await, request_counter)
|
||||
.await?;
|
||||
let mod_user: ApubPerson = mod_id.dereference(data).await?;
|
||||
|
||||
if !current_moderators
|
||||
.iter()
|
||||
@ -112,18 +86,16 @@ impl ApubObject for ApubCommunityModerators {
|
||||
.any(|x| x == mod_user.actor_id)
|
||||
{
|
||||
let community_moderator_form = CommunityModeratorForm {
|
||||
community_id: data.0.id,
|
||||
community_id: owner.id,
|
||||
person_id: mod_user.id,
|
||||
};
|
||||
CommunityModerator::join(data.1.pool(), &community_moderator_form).await?;
|
||||
CommunityModerator::join(data.pool(), &community_moderator_form).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// This return value is unused, so just set an empty vec
|
||||
Ok(ApubCommunityModerators(Vec::new()))
|
||||
}
|
||||
|
||||
type DbType = ();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -181,15 +153,13 @@ mod tests {
|
||||
let json: GroupModerators =
|
||||
file_to_json_object("assets/lemmy/collections/group_moderators.json").unwrap();
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
||||
let mut request_counter = 0;
|
||||
let community_context = CommunityContext(community, context.clone());
|
||||
ApubCommunityModerators::verify(&json, &url, &community_context, &mut request_counter)
|
||||
ApubCommunityModerators::verify(&json, &url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubCommunityModerators::from_apub(json, &community_context, &mut request_counter)
|
||||
ApubCommunityModerators::from_json(json, &community, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(request_counter, 0);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
let current_moderators = CommunityModeratorView::for_community(context.pool(), community_id)
|
||||
.await
|
||||
@ -200,7 +170,7 @@ mod tests {
|
||||
|
||||
Person::delete(context.pool(), old_mod.id).await.unwrap();
|
||||
Person::delete(context.pool(), new_mod.id).await.unwrap();
|
||||
Community::delete(context.pool(), community_context.0.id)
|
||||
Community::delete(context.pool(), community.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Site::delete(context.pool(), site.id).await.unwrap();
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
activity_lists::AnnouncableActivities,
|
||||
collections::CommunityContext,
|
||||
objects::post::ApubPost,
|
||||
objects::{community::ApubCommunity, post::ApubPost},
|
||||
protocol::{
|
||||
activities::{
|
||||
community::announce::AnnounceActivity,
|
||||
@ -12,14 +11,13 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
data::Data,
|
||||
traits::{ActivityHandler, ApubObject},
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
kinds::collection::OrderedCollectionType,
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::{ActivityHandler, Collection},
|
||||
};
|
||||
use activitystreams_kinds::collection::OrderedCollectionType;
|
||||
use chrono::NaiveDateTime;
|
||||
use futures::future::join_all;
|
||||
use lemmy_api_common::utils::generate_outbox_url;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url};
|
||||
use lemmy_db_schema::{
|
||||
source::{person::Person, post::Post},
|
||||
traits::Crud,
|
||||
@ -31,52 +29,36 @@ use url::Url;
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubCommunityOutbox {
|
||||
type DataType = CommunityContext;
|
||||
type ApubType = GroupOutbox;
|
||||
#[async_trait::async_trait]
|
||||
impl Collection for ApubCommunityOutbox {
|
||||
type Owner = ApubCommunity;
|
||||
type DataType = LemmyContext;
|
||||
type Kind = GroupOutbox;
|
||||
type Error = LemmyError;
|
||||
type DbType = ();
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
None
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
_object_id: Url,
|
||||
data: &Self::DataType,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
// Only read from database if its a local community, otherwise fetch over http
|
||||
if data.0.local {
|
||||
let community_id = data.0.id;
|
||||
let post_list: Vec<ApubPost> = Post::list_for_community(data.1.pool(), community_id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
Ok(Some(ApubCommunityOutbox(post_list)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||
async fn read_local(
|
||||
owner: &Self::Owner,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Self::Kind, LemmyError> {
|
||||
let post_list: Vec<ApubPost> = Post::list_for_community(data.pool(), owner.id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
let mut ordered_items = vec![];
|
||||
for post in self.0 {
|
||||
let person = Person::read(data.1.pool(), post.creator_id).await?.into();
|
||||
for post in post_list {
|
||||
let person = Person::read(data.pool(), post.creator_id).await?.into();
|
||||
let create =
|
||||
CreateOrUpdatePage::new(post, &person, &data.0, CreateOrUpdateType::Create, &data.1)
|
||||
.await?;
|
||||
CreateOrUpdatePage::new(post, &person, owner, CreateOrUpdateType::Create, data).await?;
|
||||
let announcable = AnnouncableActivities::CreateOrUpdatePost(create);
|
||||
let announce = AnnounceActivity::new(announcable.try_into()?, &data.0, &data.1)?;
|
||||
let announce = AnnounceActivity::new(announcable.try_into()?, owner, data)?;
|
||||
ordered_items.push(announce);
|
||||
}
|
||||
|
||||
Ok(GroupOutbox {
|
||||
r#type: OrderedCollectionType::OrderedCollection,
|
||||
id: generate_outbox_url(&data.0.actor_id)?.into(),
|
||||
id: generate_outbox_url(&owner.actor_id)?.into(),
|
||||
total_items: ordered_items.len() as i32,
|
||||
ordered_items,
|
||||
})
|
||||
@ -86,18 +68,17 @@ impl ApubObject for ApubCommunityOutbox {
|
||||
async fn verify(
|
||||
group_outbox: &GroupOutbox,
|
||||
expected_domain: &Url,
|
||||
_context: &CommunityContext,
|
||||
_request_counter: &mut i32,
|
||||
_data: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_domains_match(expected_domain, &group_outbox.id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
apub: Self::ApubType,
|
||||
data: &Self::DataType,
|
||||
_request_counter: &mut i32,
|
||||
async fn from_json(
|
||||
apub: Self::Kind,
|
||||
_owner: &Self::Owner,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Self, LemmyError> {
|
||||
let mut outbox_activities = apub.ordered_items;
|
||||
if outbox_activities.len() as i64 > FETCH_LIMIT_MAX {
|
||||
@ -110,16 +91,14 @@ impl ApubObject for ApubCommunityOutbox {
|
||||
// We intentionally ignore errors here. This is because the outbox might contain posts from old
|
||||
// Lemmy versions, or from other software which we cant parse. In that case, we simply skip the
|
||||
// item and only parse the ones that work.
|
||||
let data = Data::new(data.1.clone());
|
||||
// process items in parallel, to avoid long delay from fetch_site_metadata() and other processing
|
||||
join_all(outbox_activities.into_iter().map(|activity| {
|
||||
async {
|
||||
// use separate request counter for each item, otherwise there will be problems with
|
||||
// parallel processing
|
||||
let request_counter = &mut 0;
|
||||
let verify = activity.verify(&data, request_counter).await;
|
||||
let verify = activity.verify(data).await;
|
||||
if verify.is_ok() {
|
||||
activity.receive(&data, request_counter).await.ok();
|
||||
activity.receive(data).await.ok();
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
@ -1,9 +1,3 @@
|
||||
use crate::objects::community::ApubCommunity;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
|
||||
pub(crate) mod community_featured;
|
||||
pub(crate) mod community_moderators;
|
||||
pub(crate) mod community_outbox;
|
||||
|
||||
/// Put community in the data, so we dont have to read it again from the database.
|
||||
pub(crate) struct CommunityContext(pub ApubCommunity, pub LemmyContext);
|
||||
|
@ -1,31 +1,38 @@
|
||||
use crate::{fetcher::webfinger::webfinger_resolve_actor, ActorType};
|
||||
use activitypub_federation::traits::ApubObject;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::webfinger::webfinger_resolve_actor,
|
||||
traits::{Actor, Object},
|
||||
};
|
||||
use diesel::NotFound;
|
||||
use itertools::Itertools;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::traits::ApubActor;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
||||
pub mod post_or_comment;
|
||||
pub mod search;
|
||||
pub mod user_or_community;
|
||||
pub mod webfinger;
|
||||
|
||||
/// Resolve actor identifier (eg `!news@example.com`) from local database to avoid network requests.
|
||||
/// This only works for local actors, and remote actors which were previously fetched (so it doesnt
|
||||
/// trigger any new fetch).
|
||||
/// Resolve actor identifier like `!news@example.com` to user or community object.
|
||||
///
|
||||
/// In case the requesting user is logged in and the object was not found locally, it is attempted
|
||||
/// to fetch via webfinger from the original instance.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn resolve_actor_identifier<Actor, DbActor>(
|
||||
pub async fn resolve_actor_identifier<ActorType, DbActor>(
|
||||
identifier: &str,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
local_user_view: &Option<LocalUserView>,
|
||||
include_deleted: bool,
|
||||
) -> Result<DbActor, LemmyError>
|
||||
) -> Result<ActorType, LemmyError>
|
||||
where
|
||||
Actor: ApubObject<DataType = LemmyContext, Error = LemmyError>
|
||||
+ ApubObject<DbType = DbActor>
|
||||
+ ActorType
|
||||
ActorType: Object<DataType = LemmyContext, Error = LemmyError>
|
||||
+ Object
|
||||
+ Actor
|
||||
+ From<DbActor>
|
||||
+ Send
|
||||
+ 'static,
|
||||
for<'de2> <Actor as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
||||
for<'de2> <ActorType as Object>::Kind: serde::Deserialize<'de2>,
|
||||
DbActor: ApubActor + Send + 'static,
|
||||
{
|
||||
// remote actor
|
||||
@ -38,19 +45,22 @@ where
|
||||
let domain = format!("{}://{}", context.settings().get_protocol_string(), domain);
|
||||
let actor = DbActor::read_from_name_and_domain(context.pool(), &name, &domain).await;
|
||||
if actor.is_ok() {
|
||||
Ok(actor?)
|
||||
} else {
|
||||
Ok(actor?.into())
|
||||
} else if local_user_view.is_some() {
|
||||
// Fetch the actor from its home instance using webfinger
|
||||
let id = webfinger_resolve_actor::<Actor>(identifier, true, context, &mut 0).await?;
|
||||
let actor: DbActor = DbActor::read_from_apub_id(context.pool(), &id)
|
||||
.await?
|
||||
.expect("actor exists as we fetched just before");
|
||||
let actor: ActorType = webfinger_resolve_actor(identifier, context).await?;
|
||||
Ok(actor)
|
||||
} else {
|
||||
Err(NotFound.into())
|
||||
}
|
||||
}
|
||||
// local actor
|
||||
else {
|
||||
let identifier = identifier.to_string();
|
||||
Ok(DbActor::read_from_name(context.pool(), &identifier, include_deleted).await?)
|
||||
Ok(
|
||||
DbActor::read_from_name(context.pool(), &identifier, include_deleted)
|
||||
.await?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
InCommunity,
|
||||
},
|
||||
};
|
||||
use activitypub_federation::traits::ApubObject;
|
||||
use activitypub_federation::{config::Data, traits::Object};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
@ -29,11 +29,10 @@ pub enum PageOrNote {
|
||||
Note(Note),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for PostOrComment {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for PostOrComment {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = PageOrNote;
|
||||
type DbType = ();
|
||||
type Kind = PageOrNote;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -41,68 +40,55 @@ impl ApubObject for PostOrComment {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
data: &Self::DataType,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?;
|
||||
let post = ApubPost::read_from_id(object_id.clone(), data).await?;
|
||||
Ok(match post {
|
||||
Some(o) => Some(PostOrComment::Post(o)),
|
||||
None => ApubComment::read_from_apub_id(object_id, data)
|
||||
None => ApubComment::read_from_id(object_id, data)
|
||||
.await?
|
||||
.map(PostOrComment::Comment),
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
|
||||
async fn delete(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
match self {
|
||||
PostOrComment::Post(p) => p.delete(data).await,
|
||||
PostOrComment::Comment(c) => c.delete(data).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
apub: &Self::ApubType,
|
||||
apub: &Self::Kind,
|
||||
expected_domain: &Url,
|
||||
data: &Self::DataType,
|
||||
request_counter: &mut i32,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
match apub {
|
||||
PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data, request_counter).await,
|
||||
PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data, request_counter).await,
|
||||
PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data).await,
|
||||
PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
apub: PageOrNote,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<Self, LemmyError> {
|
||||
async fn from_json(apub: PageOrNote, context: &Data<LemmyContext>) -> Result<Self, LemmyError> {
|
||||
Ok(match apub {
|
||||
PageOrNote::Page(p) => {
|
||||
PostOrComment::Post(ApubPost::from_apub(*p, context, request_counter).await?)
|
||||
}
|
||||
PageOrNote::Note(n) => {
|
||||
PostOrComment::Comment(ApubComment::from_apub(n, context, request_counter).await?)
|
||||
}
|
||||
PageOrNote::Page(p) => PostOrComment::Post(ApubPost::from_json(*p, context).await?),
|
||||
PageOrNote::Note(n) => PostOrComment::Comment(ApubComment::from_json(n, context).await?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for PostOrComment {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
_: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let cid = match self {
|
||||
PostOrComment::Post(p) => p.community_id,
|
||||
PostOrComment::Comment(c) => Post::read(context.pool(), c.post_id).await?.community_id,
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::{
|
||||
fetcher::webfinger::webfinger_resolve_actor,
|
||||
local_instance,
|
||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||
protocol::objects::{group::Group, note::Note, page::Page, person::Person},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
|
||||
traits::Object,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -17,39 +19,29 @@ use url::Url;
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn search_query_to_object_id(
|
||||
query: &str,
|
||||
local_only: bool,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<SearchableObjects, LemmyError> {
|
||||
let request_counter = &mut 0;
|
||||
let object_id = match Url::parse(query) {
|
||||
// its already an url, just go with it
|
||||
Ok(url) => ObjectId::new(url),
|
||||
Ok(match Url::parse(query) {
|
||||
Ok(url) => {
|
||||
// its already an url, just go with it
|
||||
ObjectId::from(url).dereference(context).await?
|
||||
}
|
||||
Err(_) => {
|
||||
// not an url, try to resolve via webfinger
|
||||
let mut chars = query.chars();
|
||||
let kind = chars.next();
|
||||
let identifier = chars.as_str();
|
||||
let id = match kind {
|
||||
Some('@') => {
|
||||
webfinger_resolve_actor::<ApubPerson>(identifier, local_only, context, request_counter)
|
||||
.await?
|
||||
}
|
||||
Some('!') => {
|
||||
webfinger_resolve_actor::<ApubCommunity>(identifier, local_only, context, request_counter)
|
||||
.await?
|
||||
}
|
||||
match kind {
|
||||
Some('@') => SearchableObjects::Person(
|
||||
webfinger_resolve_actor::<LemmyContext, ApubPerson>(identifier, context).await?,
|
||||
),
|
||||
Some('!') => SearchableObjects::Community(
|
||||
webfinger_resolve_actor::<LemmyContext, ApubCommunity>(identifier, context).await?,
|
||||
),
|
||||
_ => return Err(LemmyError::from_message("invalid query")),
|
||||
};
|
||||
ObjectId::new(id)
|
||||
}
|
||||
}
|
||||
};
|
||||
if local_only {
|
||||
object_id.dereference_local(context).await
|
||||
} else {
|
||||
object_id
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
|
||||
@ -63,18 +55,17 @@ pub(crate) enum SearchableObjects {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub(crate) enum SearchableApubTypes {
|
||||
pub(crate) enum SearchableKinds {
|
||||
Group(Group),
|
||||
Person(Person),
|
||||
Page(Page),
|
||||
Note(Note),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for SearchableObjects {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for SearchableObjects {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = SearchableApubTypes;
|
||||
type DbType = ();
|
||||
type Kind = SearchableKinds;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -92,23 +83,23 @@ impl ApubObject for SearchableObjects {
|
||||
// we could skip this and always return an error, but then it would always fetch objects
|
||||
// over http, and not be able to mark objects as deleted that were deleted by remote server.
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
let c = ApubCommunity::read_from_apub_id(object_id.clone(), context).await?;
|
||||
let c = ApubCommunity::read_from_id(object_id.clone(), context).await?;
|
||||
if let Some(c) = c {
|
||||
return Ok(Some(SearchableObjects::Community(c)));
|
||||
}
|
||||
let p = ApubPerson::read_from_apub_id(object_id.clone(), context).await?;
|
||||
let p = ApubPerson::read_from_id(object_id.clone(), context).await?;
|
||||
if let Some(p) = p {
|
||||
return Ok(Some(SearchableObjects::Person(p)));
|
||||
}
|
||||
let p = ApubPost::read_from_apub_id(object_id.clone(), context).await?;
|
||||
let p = ApubPost::read_from_id(object_id.clone(), context).await?;
|
||||
if let Some(p) = p {
|
||||
return Ok(Some(SearchableObjects::Post(p)));
|
||||
}
|
||||
let c = ApubComment::read_from_apub_id(object_id, context).await?;
|
||||
let c = ApubComment::read_from_id(object_id, context).await?;
|
||||
if let Some(c) = c {
|
||||
return Ok(Some(SearchableObjects::Comment(c)));
|
||||
}
|
||||
@ -116,7 +107,7 @@ impl ApubObject for SearchableObjects {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
|
||||
async fn delete(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
match self {
|
||||
SearchableObjects::Person(p) => p.delete(data).await,
|
||||
SearchableObjects::Community(c) => c.delete(data).await,
|
||||
@ -125,46 +116,33 @@ impl ApubObject for SearchableObjects {
|
||||
}
|
||||
}
|
||||
|
||||
async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
apub: &Self::ApubType,
|
||||
apub: &Self::Kind,
|
||||
expected_domain: &Url,
|
||||
data: &Self::DataType,
|
||||
request_counter: &mut i32,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
match apub {
|
||||
SearchableApubTypes::Group(a) => {
|
||||
ApubCommunity::verify(a, expected_domain, data, request_counter).await
|
||||
}
|
||||
SearchableApubTypes::Person(a) => {
|
||||
ApubPerson::verify(a, expected_domain, data, request_counter).await
|
||||
}
|
||||
SearchableApubTypes::Page(a) => {
|
||||
ApubPost::verify(a, expected_domain, data, request_counter).await
|
||||
}
|
||||
SearchableApubTypes::Note(a) => {
|
||||
ApubComment::verify(a, expected_domain, data, request_counter).await
|
||||
}
|
||||
SearchableKinds::Group(a) => ApubCommunity::verify(a, expected_domain, data).await,
|
||||
SearchableKinds::Person(a) => ApubPerson::verify(a, expected_domain, data).await,
|
||||
SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await,
|
||||
SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
apub: Self::ApubType,
|
||||
context: &LemmyContext,
|
||||
rc: &mut i32,
|
||||
) -> Result<Self, LemmyError> {
|
||||
use SearchableApubTypes as SAT;
|
||||
async fn from_json(apub: Self::Kind, context: &Data<LemmyContext>) -> Result<Self, LemmyError> {
|
||||
use SearchableKinds as SAT;
|
||||
use SearchableObjects as SO;
|
||||
Ok(match apub {
|
||||
SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, rc).await?),
|
||||
SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, rc).await?),
|
||||
SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, rc).await?),
|
||||
SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, rc).await?),
|
||||
SAT::Group(g) => SO::Community(ApubCommunity::from_json(g, context).await?),
|
||||
SAT::Person(p) => SO::Person(ApubPerson::from_json(p, context).await?),
|
||||
SAT::Page(p) => SO::Post(ApubPost::from_json(p, context).await?),
|
||||
SAT::Note(n) => SO::Comment(ApubComment::from_json(n, context).await?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::objects::{group::Group, person::Person},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::traits::{Actor, ApubObject};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
traits::{Actor, Object},
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -29,11 +31,10 @@ pub enum PersonOrGroupType {
|
||||
Group,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for UserOrCommunity {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for UserOrCommunity {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = PersonOrGroup;
|
||||
type DbType = ();
|
||||
type Kind = PersonOrGroup;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -44,70 +45,73 @@ impl ApubObject for UserOrCommunity {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
data: &Self::DataType,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
let person = ApubPerson::read_from_apub_id(object_id.clone(), data).await?;
|
||||
let person = ApubPerson::read_from_id(object_id.clone(), data).await?;
|
||||
Ok(match person {
|
||||
Some(o) => Some(UserOrCommunity::User(o)),
|
||||
None => ApubCommunity::read_from_apub_id(object_id, data)
|
||||
None => ApubCommunity::read_from_id(object_id, data)
|
||||
.await?
|
||||
.map(UserOrCommunity::Community),
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
|
||||
async fn delete(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
match self {
|
||||
UserOrCommunity::User(p) => p.delete(data).await,
|
||||
UserOrCommunity::Community(p) => p.delete(data).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
apub: &Self::ApubType,
|
||||
apub: &Self::Kind,
|
||||
expected_domain: &Url,
|
||||
data: &Self::DataType,
|
||||
request_counter: &mut i32,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
match apub {
|
||||
PersonOrGroup::Person(a) => {
|
||||
ApubPerson::verify(a, expected_domain, data, request_counter).await
|
||||
}
|
||||
PersonOrGroup::Group(a) => {
|
||||
ApubCommunity::verify(a, expected_domain, data, request_counter).await
|
||||
}
|
||||
PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await,
|
||||
PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
apub: Self::ApubType,
|
||||
data: &Self::DataType,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<Self, LemmyError> {
|
||||
async fn from_json(apub: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, LemmyError> {
|
||||
Ok(match apub {
|
||||
PersonOrGroup::Person(p) => {
|
||||
UserOrCommunity::User(ApubPerson::from_apub(p, data, request_counter).await?)
|
||||
}
|
||||
PersonOrGroup::Person(p) => UserOrCommunity::User(ApubPerson::from_json(p, data).await?),
|
||||
PersonOrGroup::Group(p) => {
|
||||
UserOrCommunity::Community(ApubCommunity::from_apub(p, data, request_counter).await?)
|
||||
UserOrCommunity::Community(ApubCommunity::from_json(p, data).await?)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for UserOrCommunity {
|
||||
fn public_key(&self) -> &str {
|
||||
fn id(&self) -> Url {
|
||||
match self {
|
||||
UserOrCommunity::User(p) => p.public_key(),
|
||||
UserOrCommunity::Community(p) => p.public_key(),
|
||||
UserOrCommunity::User(u) => u.id(),
|
||||
UserOrCommunity::Community(c) => c.id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn public_key_pem(&self) -> &str {
|
||||
match self {
|
||||
UserOrCommunity::User(p) => p.public_key_pem(),
|
||||
UserOrCommunity::Community(p) => p.public_key_pem(),
|
||||
}
|
||||
}
|
||||
|
||||
fn private_key_pem(&self) -> Option<String> {
|
||||
match self {
|
||||
UserOrCommunity::User(p) => p.private_key_pem(),
|
||||
UserOrCommunity::Community(p) => p.private_key_pem(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,19 +119,3 @@ impl Actor for UserOrCommunity {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActorType for UserOrCommunity {
|
||||
fn actor_id(&self) -> Url {
|
||||
match self {
|
||||
UserOrCommunity::User(u) => u.actor_id(),
|
||||
UserOrCommunity::Community(c) => c.actor_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn private_key(&self) -> Option<String> {
|
||||
match self {
|
||||
UserOrCommunity::User(u) => u.private_key(),
|
||||
UserOrCommunity::Community(c) => c.private_key(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
use crate::{local_instance, ActorType, FEDERATION_HTTP_FETCH_LIMIT};
|
||||
use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
|
||||
use anyhow::anyhow;
|
||||
use itertools::Itertools;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::newtypes::DbUrl;
|
||||
use lemmy_utils::{error::LemmyError, WebfingerResponse};
|
||||
use tracing::debug;
|
||||
use url::Url;
|
||||
|
||||
/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
|
||||
/// using webfinger.
|
||||
pub(crate) async fn webfinger_resolve_actor<Kind>(
|
||||
identifier: &str,
|
||||
local_only: bool,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<DbUrl, LemmyError>
|
||||
where
|
||||
Kind: ApubObject<DataType = LemmyContext, Error = LemmyError> + ActorType + Send + 'static,
|
||||
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
||||
{
|
||||
let protocol = context.settings().get_protocol_string();
|
||||
let (_, domain) = identifier
|
||||
.splitn(2, '@')
|
||||
.collect_tuple()
|
||||
.ok_or_else(|| LemmyError::from_message("Invalid webfinger query, missing domain"))?;
|
||||
let fetch_url = format!("{protocol}://{domain}/.well-known/webfinger?resource=acct:{identifier}");
|
||||
debug!("Fetching webfinger url: {}", &fetch_url);
|
||||
|
||||
*request_counter += 1;
|
||||
if *request_counter > FEDERATION_HTTP_FETCH_LIMIT {
|
||||
return Err(LemmyError::from_message("Request retry limit reached"));
|
||||
}
|
||||
|
||||
let response = context.client().get(&fetch_url).send().await?;
|
||||
|
||||
let res: WebfingerResponse = response.json().await.map_err(LemmyError::from)?;
|
||||
|
||||
let links: Vec<Url> = res
|
||||
.links
|
||||
.iter()
|
||||
.filter(|link| {
|
||||
if let Some(type_) = &link.kind {
|
||||
type_.starts_with("application/")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.filter_map(|l| l.href.clone())
|
||||
.collect();
|
||||
for l in links {
|
||||
let object_id = ObjectId::<Kind>::new(l);
|
||||
let object = if local_only {
|
||||
object_id.dereference_local(context).await
|
||||
} else {
|
||||
object_id
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await
|
||||
};
|
||||
if object.is_ok() {
|
||||
return object.map(|o| o.actor_id().into());
|
||||
}
|
||||
}
|
||||
let err = anyhow!("Failed to resolve actor for {}", identifier);
|
||||
Err(LemmyError::from_error_message(err, "failed_to_resolve"))
|
||||
}
|
@ -2,8 +2,8 @@ use crate::{
|
||||
http::{create_apub_response, create_apub_tombstone_response, err_object_not_local},
|
||||
objects::comment::ApubComment,
|
||||
};
|
||||
use activitypub_federation::traits::ApubObject;
|
||||
use actix_web::{web, web::Path, HttpResponse};
|
||||
use activitypub_federation::{config::Data, traits::Object};
|
||||
use actix_web::{web::Path, HttpResponse};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -18,7 +18,7 @@ pub(crate) struct CommentQuery {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_comment(
|
||||
info: Path<CommentQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let id = CommentId(info.comment_id.parse::<i32>()?);
|
||||
let comment: ApubComment = Comment::read(context.pool(), id).await?.into();
|
||||
@ -27,7 +27,7 @@ pub(crate) async fn get_apub_comment(
|
||||
}
|
||||
|
||||
if !comment.deleted && !comment.removed {
|
||||
Ok(create_apub_response(&comment.into_apub(&context).await?))
|
||||
Ok(create_apub_response(&comment.into_json(&context).await?))
|
||||
} else {
|
||||
Ok(create_apub_tombstone_response(comment.ap_id.clone()))
|
||||
}
|
||||
|
@ -4,23 +4,19 @@ use crate::{
|
||||
community_featured::ApubCommunityFeatured,
|
||||
community_moderators::ApubCommunityModerators,
|
||||
community_outbox::ApubCommunityOutbox,
|
||||
CommunityContext,
|
||||
},
|
||||
http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity},
|
||||
local_instance,
|
||||
http::{create_apub_response, create_apub_tombstone_response},
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::collections::group_followers::GroupFollowers,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
deser::context::WithContext,
|
||||
traits::ApubObject,
|
||||
};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::{generate_featured_url, generate_outbox_url},
|
||||
actix_web::inbox::receive_activity,
|
||||
config::Data,
|
||||
protocol::context::WithContext,
|
||||
traits::{Collection, Object},
|
||||
};
|
||||
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{source::community::Community, traits::ApubActor};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::Deserialize;
|
||||
@ -34,7 +30,7 @@ pub(crate) struct CommunityQuery {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_community_http(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community: ApubCommunity =
|
||||
Community::read_from_name(context.pool(), &info.community_name, true)
|
||||
@ -42,7 +38,7 @@ pub(crate) async fn get_apub_community_http(
|
||||
.into();
|
||||
|
||||
if !community.deleted && !community.removed {
|
||||
let apub = community.into_apub(&context).await?;
|
||||
let apub = community.into_json(&context).await?;
|
||||
|
||||
Ok(create_apub_response(&apub))
|
||||
} else {
|
||||
@ -54,17 +50,19 @@ pub(crate) async fn get_apub_community_http(
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn community_inbox(
|
||||
request: HttpRequest,
|
||||
payload: String,
|
||||
context: web::Data<LemmyContext>,
|
||||
body: Bytes,
|
||||
data: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
receive_lemmy_activity::<WithContext<GroupInboxActivities>, ApubPerson>(request, payload, context)
|
||||
.await
|
||||
receive_activity::<WithContext<GroupInboxActivities>, ApubPerson, LemmyContext>(
|
||||
request, body, &data,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns an empty followers collection, only populating the size (for privacy).
|
||||
pub(crate) async fn get_apub_community_followers(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community = Community::read_from_name(context.pool(), &info.community_name, false).await?;
|
||||
let followers = GroupFollowers::new(community, &context).await?;
|
||||
@ -75,24 +73,7 @@ pub(crate) async fn get_apub_community_followers(
|
||||
/// activites like votes or comments).
|
||||
pub(crate) async fn get_apub_community_outbox(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community = Community::read_from_name(context.pool(), &info.community_name, false).await?;
|
||||
if community.deleted || community.removed {
|
||||
return Err(LemmyError::from_message("deleted"));
|
||||
}
|
||||
let id = ObjectId::new(generate_outbox_url(&community.actor_id)?);
|
||||
let outbox_data = CommunityContext(community.into(), context.get_ref().clone());
|
||||
let outbox: ApubCommunityOutbox = id
|
||||
.dereference(&outbox_data, local_instance(&context).await, &mut 0)
|
||||
.await?;
|
||||
Ok(create_apub_response(&outbox.into_apub(&outbox_data).await?))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_community_moderators(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community: ApubCommunity =
|
||||
Community::read_from_name(context.pool(), &info.community_name, false)
|
||||
@ -101,29 +82,38 @@ pub(crate) async fn get_apub_community_moderators(
|
||||
if community.deleted || community.removed {
|
||||
return Err(LemmyError::from_message("deleted"));
|
||||
}
|
||||
let id = ObjectId::new(generate_outbox_url(&community.actor_id)?);
|
||||
let outbox_data = CommunityContext(community, context.get_ref().clone());
|
||||
let moderators: ApubCommunityModerators = id
|
||||
.dereference(&outbox_data, local_instance(&context).await, &mut 0)
|
||||
.await?;
|
||||
Ok(create_apub_response(
|
||||
&moderators.into_apub(&outbox_data).await?,
|
||||
))
|
||||
let outbox = ApubCommunityOutbox::read_local(&community, &context).await?;
|
||||
Ok(create_apub_response(&outbox))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_community_moderators(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community: ApubCommunity =
|
||||
Community::read_from_name(context.pool(), &info.community_name, false)
|
||||
.await?
|
||||
.into();
|
||||
if community.deleted || community.removed {
|
||||
return Err(LemmyError::from_message("deleted"));
|
||||
}
|
||||
let moderators = ApubCommunityModerators::read_local(&community, &context).await?;
|
||||
Ok(create_apub_response(&moderators))
|
||||
}
|
||||
|
||||
/// Returns collection of featured (stickied) posts.
|
||||
pub(crate) async fn get_apub_community_featured(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let community = Community::read_from_name(context.pool(), &info.community_name, false).await?;
|
||||
let community: ApubCommunity =
|
||||
Community::read_from_name(context.pool(), &info.community_name, false)
|
||||
.await?
|
||||
.into();
|
||||
if community.deleted || community.removed {
|
||||
return Err(LemmyError::from_message("deleted"));
|
||||
}
|
||||
let id = ObjectId::new(generate_featured_url(&community.actor_id)?);
|
||||
let data = CommunityContext(community.into(), context.get_ref().clone());
|
||||
let featured: ApubCommunityFeatured = id
|
||||
.dereference(&data, local_instance(&context).await, &mut 0)
|
||||
.await?;
|
||||
Ok(create_apub_response(&featured.into_apub(&data).await?))
|
||||
let featured = ApubCommunityFeatured::read_local(&community, &context).await?;
|
||||
Ok(create_apub_response(&featured))
|
||||
}
|
||||
|
@ -1,28 +1,22 @@
|
||||
use crate::{
|
||||
activity_lists::SharedInboxActivities,
|
||||
fetcher::user_or_community::UserOrCommunity,
|
||||
insert_activity,
|
||||
local_instance,
|
||||
protocol::objects::tombstone::Tombstone,
|
||||
CONTEXT,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::inbox::receive_activity,
|
||||
data::Data,
|
||||
deser::context::WithContext,
|
||||
traits::{ActivityHandler, Actor, ApubObject},
|
||||
APUB_JSON_CONTENT_TYPE,
|
||||
actix_web::inbox::receive_activity,
|
||||
config::Data,
|
||||
protocol::context::WithContext,
|
||||
FEDERATION_CONTENT_TYPE,
|
||||
};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
|
||||
use http::StatusCode;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::source::activity::Activity;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Deref;
|
||||
use tracing::{debug, log::info};
|
||||
use url::Url;
|
||||
|
||||
mod comment;
|
||||
@ -34,45 +28,11 @@ pub mod site;
|
||||
|
||||
pub async fn shared_inbox(
|
||||
request: HttpRequest,
|
||||
payload: String,
|
||||
context: web::Data<LemmyContext>,
|
||||
body: Bytes,
|
||||
data: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
receive_lemmy_activity::<SharedInboxActivities, UserOrCommunity>(request, payload, context).await
|
||||
}
|
||||
|
||||
pub async fn receive_lemmy_activity<Activity, ActorT>(
|
||||
request: HttpRequest,
|
||||
payload: String,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError>
|
||||
where
|
||||
Activity: ActivityHandler<DataType = LemmyContext, Error = LemmyError>
|
||||
+ DeserializeOwned
|
||||
+ Send
|
||||
+ 'static,
|
||||
ActorT: ApubObject<DataType = LemmyContext, Error = LemmyError> + Actor + Send + 'static,
|
||||
for<'de2> <ActorT as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
||||
{
|
||||
static DATA: OnceCell<Data<LemmyContext>> = OnceCell::new();
|
||||
let activity_value: Value = serde_json::from_str(&payload)?;
|
||||
debug!("Parsing activity {}", payload);
|
||||
let activity: Activity = serde_json::from_value(activity_value.clone())?;
|
||||
// Log the activity, so we avoid receiving and parsing it twice.
|
||||
let insert = insert_activity(activity.id(), activity_value, false, true, context.pool()).await?;
|
||||
if !insert {
|
||||
debug!("Received duplicate activity {}", activity.id().to_string());
|
||||
return Ok(HttpResponse::BadRequest().finish());
|
||||
}
|
||||
info!("Received activity {}", payload);
|
||||
|
||||
let data = DATA.get_or_init(|| Data::new(context.get_ref().clone()));
|
||||
receive_activity::<Activity, ActorT, LemmyContext>(
|
||||
request,
|
||||
activity,
|
||||
local_instance(&context).await,
|
||||
data,
|
||||
)
|
||||
.await
|
||||
receive_activity::<SharedInboxActivities, UserOrCommunity, LemmyContext>(request, body, &data)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
|
||||
@ -82,20 +42,20 @@ where
|
||||
T: Serialize,
|
||||
{
|
||||
HttpResponse::Ok()
|
||||
.content_type(APUB_JSON_CONTENT_TYPE)
|
||||
.content_type(FEDERATION_CONTENT_TYPE)
|
||||
.json(WithContext::new(data, CONTEXT.deref().clone()))
|
||||
}
|
||||
|
||||
fn create_json_apub_response(data: serde_json::Value) -> HttpResponse {
|
||||
HttpResponse::Ok()
|
||||
.content_type(APUB_JSON_CONTENT_TYPE)
|
||||
.content_type(FEDERATION_CONTENT_TYPE)
|
||||
.json(data)
|
||||
}
|
||||
|
||||
fn create_apub_tombstone_response<T: Into<Url>>(id: T) -> HttpResponse {
|
||||
let tombstone = Tombstone::new(id.into());
|
||||
HttpResponse::Gone()
|
||||
.content_type(APUB_JSON_CONTENT_TYPE)
|
||||
.content_type(FEDERATION_CONTENT_TYPE)
|
||||
.status(StatusCode::GONE)
|
||||
.json(WithContext::new(tombstone, CONTEXT.deref().clone()))
|
||||
}
|
||||
|
@ -1,12 +1,17 @@
|
||||
use crate::{
|
||||
activity_lists::PersonInboxActivitiesWithAnnouncable,
|
||||
fetcher::user_or_community::UserOrCommunity,
|
||||
http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity},
|
||||
http::{create_apub_response, create_apub_tombstone_response},
|
||||
objects::person::ApubPerson,
|
||||
protocol::collections::empty_outbox::EmptyOutbox,
|
||||
};
|
||||
use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use activitypub_federation::{
|
||||
actix_web::inbox::receive_activity,
|
||||
config::Data,
|
||||
protocol::context::WithContext,
|
||||
traits::Object,
|
||||
};
|
||||
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
|
||||
use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url};
|
||||
use lemmy_db_schema::{source::person::Person, traits::ApubActor};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -21,7 +26,7 @@ pub struct PersonQuery {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_person_http(
|
||||
info: web::Path<PersonQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let user_name = info.into_inner().user_name;
|
||||
// TODO: this needs to be able to read deleted persons, so that it can send tombstones
|
||||
@ -30,7 +35,7 @@ pub(crate) async fn get_apub_person_http(
|
||||
.into();
|
||||
|
||||
if !person.deleted {
|
||||
let apub = person.into_apub(&context).await?;
|
||||
let apub = person.into_json(&context).await?;
|
||||
|
||||
Ok(create_apub_response(&apub))
|
||||
} else {
|
||||
@ -41,11 +46,11 @@ pub(crate) async fn get_apub_person_http(
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn person_inbox(
|
||||
request: HttpRequest,
|
||||
payload: String,
|
||||
context: web::Data<LemmyContext>,
|
||||
body: Bytes,
|
||||
data: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
receive_lemmy_activity::<WithContext<PersonInboxActivitiesWithAnnouncable>, UserOrCommunity>(
|
||||
request, payload, context,
|
||||
receive_activity::<WithContext<PersonInboxActivitiesWithAnnouncable>, UserOrCommunity, LemmyContext>(
|
||||
request, body, &data,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -53,7 +58,7 @@ pub async fn person_inbox(
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_person_outbox(
|
||||
info: web::Path<PersonQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let person = Person::read_from_name(context.pool(), &info.user_name, false).await?;
|
||||
let outbox_id = generate_outbox_url(&person.actor_id)?.into();
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
http::{create_apub_response, create_apub_tombstone_response, err_object_not_local},
|
||||
objects::post::ApubPost,
|
||||
};
|
||||
use activitypub_federation::traits::ApubObject;
|
||||
use activitypub_federation::{config::Data, traits::Object};
|
||||
use actix_web::{web, HttpResponse};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud};
|
||||
@ -18,7 +18,7 @@ pub(crate) struct PostQuery {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_post(
|
||||
info: web::Path<PostQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let id = PostId(info.post_id.parse::<i32>()?);
|
||||
let post: ApubPost = Post::read(context.pool(), id).await?.into();
|
||||
@ -27,7 +27,7 @@ pub(crate) async fn get_apub_post(
|
||||
}
|
||||
|
||||
if !post.deleted && !post.removed {
|
||||
Ok(create_apub_response(&post.into_apub(&context).await?))
|
||||
Ok(create_apub_response(&post.into_json(&context).await?))
|
||||
} else {
|
||||
Ok(create_apub_tombstone_response(post.ap_id.clone()))
|
||||
}
|
||||
|
@ -1,28 +1,33 @@
|
||||
use crate::{
|
||||
activity_lists::SiteInboxActivities,
|
||||
http::{create_apub_response, receive_lemmy_activity},
|
||||
http::create_apub_response,
|
||||
objects::{instance::ApubSite, person::ApubPerson},
|
||||
protocol::collections::empty_outbox::EmptyOutbox,
|
||||
};
|
||||
use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use activitypub_federation::{
|
||||
actix_web::inbox::receive_activity,
|
||||
config::Data,
|
||||
protocol::context::WithContext,
|
||||
traits::Object,
|
||||
};
|
||||
use actix_web::{web::Bytes, HttpRequest, HttpResponse};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
pub(crate) async fn get_apub_site_http(
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let site: ApubSite = SiteView::read_local(context.pool()).await?.site.into();
|
||||
|
||||
let apub = site.into_apub(&context).await?;
|
||||
let apub = site.into_json(&context).await?;
|
||||
Ok(create_apub_response(&apub))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn get_apub_site_outbox(
|
||||
context: web::Data<LemmyContext>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let outbox_id = format!(
|
||||
"{}/site_outbox",
|
||||
@ -35,9 +40,11 @@ pub(crate) async fn get_apub_site_outbox(
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn get_apub_site_inbox(
|
||||
request: HttpRequest,
|
||||
payload: String,
|
||||
context: web::Data<LemmyContext>,
|
||||
body: Bytes,
|
||||
data: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
receive_lemmy_activity::<WithContext<SiteInboxActivities>, ApubPerson>(request, payload, context)
|
||||
.await
|
||||
receive_activity::<WithContext<SiteInboxActivities>, ApubPerson, LemmyContext>(
|
||||
request, body, &data,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
@ -1,20 +1,19 @@
|
||||
use crate::fetcher::post_or_comment::PostOrComment;
|
||||
use activitypub_federation::{
|
||||
core::signatures::PublicKey,
|
||||
traits::{Actor, ApubObject},
|
||||
InstanceSettings,
|
||||
LocalInstance,
|
||||
UrlVerifier,
|
||||
};
|
||||
use activitypub_federation::config::{Data, UrlVerifier};
|
||||
use async_trait::async_trait;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
source::{activity::Activity, instance::Instance, local_site::LocalSite},
|
||||
source::{
|
||||
activity::{Activity, ActivityInsertForm},
|
||||
instance::Instance,
|
||||
local_site::LocalSite,
|
||||
},
|
||||
traits::Crud,
|
||||
utils::DbPool,
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, settings::structs::Settings};
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::sync::OnceCell;
|
||||
use serde::Serialize;
|
||||
use url::Url;
|
||||
|
||||
pub mod activities;
|
||||
@ -27,52 +26,23 @@ pub(crate) mod mentions;
|
||||
pub mod objects;
|
||||
pub mod protocol;
|
||||
|
||||
const FEDERATION_HTTP_FETCH_LIMIT: i32 = 25;
|
||||
pub const FEDERATION_HTTP_FETCH_LIMIT: u32 = 50;
|
||||
|
||||
static CONTEXT: Lazy<Vec<serde_json::Value>> = Lazy::new(|| {
|
||||
serde_json::from_str(include_str!("../assets/lemmy/context.json")).expect("parse context")
|
||||
});
|
||||
|
||||
// TODO: store this in context? but its only used in this crate, no need to expose it elsewhere
|
||||
// TODO this singleton needs to be redone to account for live data.
|
||||
async fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
|
||||
static LOCAL_INSTANCE: OnceCell<LocalInstance> = OnceCell::const_new();
|
||||
LOCAL_INSTANCE
|
||||
.get_or_init(|| async {
|
||||
// Local site may be missing
|
||||
let local_site = &LocalSite::read(context.pool()).await;
|
||||
let worker_count = local_site
|
||||
.as_ref()
|
||||
.map(|l| l.federation_worker_count)
|
||||
.unwrap_or(64) as u64;
|
||||
|
||||
let settings = InstanceSettings::builder()
|
||||
.http_fetch_retry_limit(FEDERATION_HTTP_FETCH_LIMIT)
|
||||
.worker_count(worker_count)
|
||||
.debug(cfg!(debug_assertions))
|
||||
.http_signature_compat(true)
|
||||
.url_verifier(Box::new(VerifyUrlData(context.clone())))
|
||||
.build()
|
||||
.expect("configure federation");
|
||||
LocalInstance::new(
|
||||
context.settings().hostname.clone(),
|
||||
context.client().clone(),
|
||||
settings,
|
||||
)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct VerifyUrlData(LemmyContext);
|
||||
pub struct VerifyUrlData(pub DbPool);
|
||||
|
||||
#[async_trait]
|
||||
impl UrlVerifier for VerifyUrlData {
|
||||
async fn verify(&self, url: &Url) -> Result<(), &'static str> {
|
||||
let local_site_data = fetch_local_site_data(self.0.pool())
|
||||
let local_site_data = fetch_local_site_data(&self.0)
|
||||
.await
|
||||
.expect("read local site data");
|
||||
check_apub_id_valid(url, &local_site_data, self.0.settings())
|
||||
check_apub_id_valid(url, &local_site_data)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,19 +56,9 @@ impl UrlVerifier for VerifyUrlData {
|
||||
///
|
||||
/// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a
|
||||
/// post/comment in a local community.
|
||||
#[tracing::instrument(skip(settings, local_site_data))]
|
||||
fn check_apub_id_valid(
|
||||
apub_id: &Url,
|
||||
local_site_data: &LocalSiteData,
|
||||
settings: &Settings,
|
||||
) -> Result<(), &'static str> {
|
||||
#[tracing::instrument(skip(local_site_data))]
|
||||
fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> Result<(), &'static str> {
|
||||
let domain = apub_id.domain().expect("apud id has domain").to_string();
|
||||
let local_instance = settings
|
||||
.get_hostname_without_port()
|
||||
.expect("local hostname is valid");
|
||||
if domain == local_instance {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !local_site_data
|
||||
.local_site
|
||||
@ -109,10 +69,6 @@ fn check_apub_id_valid(
|
||||
return Err("Federation disabled");
|
||||
}
|
||||
|
||||
if apub_id.scheme() != settings.get_protocol_string() {
|
||||
return Err("Invalid protocol scheme");
|
||||
}
|
||||
|
||||
if let Some(blocked) = local_site_data.blocked_instances.as_ref() {
|
||||
if blocked.iter().any(|i| domain.eq(&i.domain)) {
|
||||
return Err("Domain is blocked");
|
||||
@ -161,7 +117,6 @@ pub(crate) fn check_apub_id_valid_with_strictness(
|
||||
local_site_data: &LocalSiteData,
|
||||
settings: &Settings,
|
||||
) -> Result<(), LemmyError> {
|
||||
check_apub_id_valid(apub_id, local_site_data, settings).map_err(LemmyError::from_message)?;
|
||||
let domain = apub_id.domain().expect("apud id has domain").to_string();
|
||||
let local_instance = settings
|
||||
.get_hostname_without_port()
|
||||
@ -169,6 +124,7 @@ pub(crate) fn check_apub_id_valid_with_strictness(
|
||||
if domain == local_instance {
|
||||
return Ok(());
|
||||
}
|
||||
check_apub_id_valid(apub_id, local_site_data).map_err(LemmyError::from_message)?;
|
||||
|
||||
if let Some(allowed) = local_site_data.allowed_instances.as_ref() {
|
||||
// Only check allowlist if this is a community
|
||||
@ -179,8 +135,12 @@ pub(crate) fn check_apub_id_valid_with_strictness(
|
||||
.iter()
|
||||
.map(|i| i.domain.clone())
|
||||
.collect::<Vec<String>>();
|
||||
let local_instance = settings
|
||||
.get_hostname_without_port()
|
||||
.expect("local hostname is valid");
|
||||
allowed_and_local.push(local_instance);
|
||||
|
||||
let domain = apub_id.domain().expect("apud id has domain").to_string();
|
||||
if !allowed_and_local.contains(&domain) {
|
||||
return Err(LemmyError::from_message(
|
||||
"Federation forbidden by strict allowlist",
|
||||
@ -191,40 +151,41 @@ pub(crate) fn check_apub_id_valid_with_strictness(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Store a sent or received activity in the database, for logging purposes. These records are not
|
||||
/// persistent.
|
||||
#[tracing::instrument(skip(pool))]
|
||||
async fn insert_activity(
|
||||
/// Store a sent or received activity in the database.
|
||||
///
|
||||
/// Stored activities are served over the HTTP endpoint `GET /activities/{type_}/{id}`. This also
|
||||
/// ensures that the same activity cannot be received more than once.
|
||||
#[tracing::instrument(skip(data, activity))]
|
||||
async fn insert_activity<T>(
|
||||
ap_id: &Url,
|
||||
activity: serde_json::Value,
|
||||
activity: &T,
|
||||
local: bool,
|
||||
sensitive: bool,
|
||||
pool: &DbPool,
|
||||
) -> Result<bool, LemmyError> {
|
||||
data: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let ap_id = ap_id.clone().into();
|
||||
Ok(Activity::insert(pool, ap_id, activity, local, Some(sensitive)).await?)
|
||||
let form = ActivityInsertForm {
|
||||
ap_id,
|
||||
data: serde_json::to_value(activity)?,
|
||||
local: Some(local),
|
||||
sensitive: Some(sensitive),
|
||||
updated: None,
|
||||
};
|
||||
Activity::create(data.pool(), &form).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Common methods provided by ActivityPub actors (community and person). Not all methods are
|
||||
/// implemented by all actors.
|
||||
pub trait ActorType: Actor + ApubObject {
|
||||
fn actor_id(&self) -> Url;
|
||||
|
||||
fn private_key(&self) -> Option<String>;
|
||||
|
||||
fn get_public_key(&self) -> PublicKey {
|
||||
PublicKey::new_main_key(self.actor_id(), self.public_key().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait SendActivity {
|
||||
type Response;
|
||||
#[async_trait::async_trait]
|
||||
pub trait SendActivity: Sync {
|
||||
type Response: Sync + Send;
|
||||
|
||||
async fn send_activity(
|
||||
_request: &Self,
|
||||
_response: &Self::Response,
|
||||
_context: &LemmyContext,
|
||||
_context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
fetcher::webfinger::webfinger_resolve_actor,
|
||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
||||
ActorType,
|
||||
use crate::objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
|
||||
kinds::link::MentionType,
|
||||
traits::Actor,
|
||||
};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::link::MentionType;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
source::{comment::Comment, person::Person, post::Post},
|
||||
@ -46,11 +46,10 @@ pub struct MentionsAndAddresses {
|
||||
pub async fn collect_non_local_mentions(
|
||||
comment: &ApubComment,
|
||||
community_id: ObjectId<ApubCommunity>,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<MentionsAndAddresses, LemmyError> {
|
||||
let parent_creator = get_comment_parent_creator(context.pool(), comment).await?;
|
||||
let mut addressed_ccs: Vec<Url> = vec![community_id.into(), parent_creator.actor_id()];
|
||||
let mut addressed_ccs: Vec<Url> = vec![community_id.into(), parent_creator.id()];
|
||||
|
||||
// Add the mention tag
|
||||
let parent_creator_tag = Mention {
|
||||
@ -58,7 +57,7 @@ pub async fn collect_non_local_mentions(
|
||||
name: Some(format!(
|
||||
"@{}@{}",
|
||||
&parent_creator.name,
|
||||
&parent_creator.actor_id().domain().expect("has domain")
|
||||
&parent_creator.id().domain().expect("has domain")
|
||||
)),
|
||||
kind: MentionType::Mention,
|
||||
};
|
||||
@ -73,14 +72,12 @@ pub async fn collect_non_local_mentions(
|
||||
|
||||
for mention in &mentions {
|
||||
let identifier = format!("{}@{}", mention.name, mention.domain);
|
||||
let actor_id =
|
||||
webfinger_resolve_actor::<ApubPerson>(&identifier, true, context, request_counter).await;
|
||||
if let Ok(actor_id) = actor_id {
|
||||
let actor_id: ObjectId<ApubPerson> = ObjectId::new(actor_id);
|
||||
addressed_ccs.push(actor_id.to_string().parse()?);
|
||||
let person = webfinger_resolve_actor::<LemmyContext, ApubPerson>(&identifier, context).await;
|
||||
if let Ok(person) = person {
|
||||
addressed_ccs.push(person.actor_id.to_string().parse()?);
|
||||
|
||||
let mention_tag = Mention {
|
||||
href: actor_id.into(),
|
||||
href: person.id(),
|
||||
name: Some(mention.full_name()),
|
||||
kind: MentionType::Mention,
|
||||
};
|
||||
|
@ -2,7 +2,6 @@ use crate::{
|
||||
activities::{verify_is_public, verify_person_in_community},
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
mentions::collect_non_local_mentions,
|
||||
objects::{read_from_string_or_source, verify_is_remote_object},
|
||||
protocol::{
|
||||
@ -10,15 +9,13 @@ use crate::{
|
||||
InCommunity,
|
||||
Source,
|
||||
},
|
||||
PostOrComment,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
deser::values::MediaTypeMarkdownOrHtml,
|
||||
traits::ApubObject,
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
kinds::{object::NoteType, public},
|
||||
protocol::{values::MediaTypeMarkdownOrHtml, verification::verify_domains_match},
|
||||
traits::Object,
|
||||
};
|
||||
use activitystreams_kinds::{object::NoteType, public};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
|
||||
use lemmy_db_schema::{
|
||||
@ -54,11 +51,10 @@ impl From<Comment> for ApubComment {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubComment {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for ApubComment {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = Note;
|
||||
type DbType = Comment;
|
||||
type Kind = Note;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -66,9 +62,9 @@ impl ApubObject for ApubComment {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
Ok(
|
||||
Comment::read_from_apub_id(context.pool(), object_id)
|
||||
@ -78,7 +74,7 @@ impl ApubObject for ApubComment {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
if !self.deleted {
|
||||
let form = CommentUpdateForm::builder().deleted(Some(true)).build();
|
||||
Comment::update(context.pool(), self.id, &form).await?;
|
||||
@ -87,7 +83,7 @@ impl ApubObject for ApubComment {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_apub(self, context: &LemmyContext) -> Result<Note, LemmyError> {
|
||||
async fn into_json(self, context: &Data<Self::DataType>) -> Result<Note, LemmyError> {
|
||||
let creator_id = self.creator_id;
|
||||
let creator = Person::read(context.pool(), creator_id).await?;
|
||||
|
||||
@ -98,23 +94,17 @@ impl ApubObject for ApubComment {
|
||||
|
||||
let in_reply_to = if let Some(comment_id) = self.parent_comment_id() {
|
||||
let parent_comment = Comment::read(context.pool(), comment_id).await?;
|
||||
ObjectId::<PostOrComment>::new(parent_comment.ap_id)
|
||||
parent_comment.ap_id.into()
|
||||
} else {
|
||||
ObjectId::<PostOrComment>::new(post.ap_id)
|
||||
post.ap_id.into()
|
||||
};
|
||||
let language = LanguageTag::new_single(self.language_id, context.pool()).await?;
|
||||
let maa = collect_non_local_mentions(
|
||||
&self,
|
||||
ObjectId::new(community.actor_id.clone()),
|
||||
context,
|
||||
&mut 0,
|
||||
)
|
||||
.await?;
|
||||
let maa = collect_non_local_mentions(&self, community.actor_id.clone().into(), context).await?;
|
||||
|
||||
let note = Note {
|
||||
r#type: NoteType::Note,
|
||||
id: ObjectId::new(self.ap_id.clone()),
|
||||
attributed_to: ObjectId::new(creator.actor_id),
|
||||
id: self.ap_id.clone().into(),
|
||||
attributed_to: creator.actor_id.into(),
|
||||
to: vec![public()],
|
||||
cc: maa.ccs,
|
||||
content: markdown_to_html(&self.content),
|
||||
@ -126,7 +116,7 @@ impl ApubObject for ApubComment {
|
||||
tag: maa.tags,
|
||||
distinguished: Some(self.distinguished),
|
||||
language,
|
||||
audience: Some(ObjectId::new(community.actor_id)),
|
||||
audience: Some(community.actor_id.into()),
|
||||
};
|
||||
|
||||
Ok(note)
|
||||
@ -136,13 +126,12 @@ impl ApubObject for ApubComment {
|
||||
async fn verify(
|
||||
note: &Note,
|
||||
expected_domain: &Url,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_domains_match(note.id.inner(), expected_domain)?;
|
||||
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
|
||||
verify_is_public(¬e.to, ¬e.cc)?;
|
||||
let community = note.community(context, request_counter).await?;
|
||||
let community = note.community(context).await?;
|
||||
let local_site_data = fetch_local_site_data(context.pool()).await?;
|
||||
|
||||
check_apub_id_valid_with_strictness(
|
||||
@ -152,8 +141,8 @@ impl ApubObject for ApubComment {
|
||||
context.settings(),
|
||||
)?;
|
||||
verify_is_remote_object(note.id.inner(), context.settings())?;
|
||||
verify_person_in_community(¬e.attributed_to, &community, context, request_counter).await?;
|
||||
let (post, _) = note.get_parents(context, request_counter).await?;
|
||||
verify_person_in_community(¬e.attributed_to, &community, context).await?;
|
||||
let (post, _) = note.get_parents(context).await?;
|
||||
if post.locked {
|
||||
return Err(LemmyError::from_message("Post is locked"));
|
||||
}
|
||||
@ -164,16 +153,9 @@ impl ApubObject for ApubComment {
|
||||
///
|
||||
/// If the parent community, post and comment(s) are not known locally, these are also fetched.
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
note: Note,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubComment, LemmyError> {
|
||||
let creator = note
|
||||
.attributed_to
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let (post, parent_comment) = note.get_parents(context, request_counter).await?;
|
||||
async fn from_json(note: Note, context: &Data<LemmyContext>) -> Result<ApubComment, LemmyError> {
|
||||
let creator = note.attributed_to.dereference(context).await?;
|
||||
let (post, parent_comment) = note.get_parents(context).await?;
|
||||
|
||||
let content = read_from_string_or_source(¬e.content, ¬e.media_type, ¬e.source);
|
||||
|
||||
@ -221,17 +203,15 @@ pub(crate) mod tests {
|
||||
|
||||
async fn prepare_comment_test(
|
||||
url: &Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> (ApubPerson, ApubCommunity, ApubPost, ApubSite) {
|
||||
let (person, site) = parse_lemmy_person(context).await;
|
||||
let community = parse_lemmy_community(context).await;
|
||||
// use separate counter so this doesnt affect tests
|
||||
let context2 = context.reset_request_count();
|
||||
let (person, site) = parse_lemmy_person(&context2).await;
|
||||
let community = parse_lemmy_community(&context2).await;
|
||||
let post_json = file_to_json_object("assets/lemmy/objects/page.json").unwrap();
|
||||
ApubPost::verify(&post_json, url, context, &mut 0)
|
||||
.await
|
||||
.unwrap();
|
||||
let post = ApubPost::from_apub(post_json, context, &mut 0)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubPost::verify(&post_json, url, &context2).await.unwrap();
|
||||
let post = ApubPost::from_json(post_json, &context2).await.unwrap();
|
||||
(person, community, post, site)
|
||||
}
|
||||
|
||||
@ -251,21 +231,18 @@ pub(crate) mod tests {
|
||||
let data = prepare_comment_test(&url, &context).await;
|
||||
|
||||
let json: Note = file_to_json_object("assets/lemmy/objects/note.json").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubComment::verify(&json, &url, &context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
let comment = ApubComment::from_apub(json.clone(), &context, &mut request_counter)
|
||||
ApubComment::verify(&json, &url, &context).await.unwrap();
|
||||
let comment = ApubComment::from_json(json.clone(), &context)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(comment.ap_id, url.into());
|
||||
assert_eq!(comment.content.len(), 14);
|
||||
assert!(!comment.local);
|
||||
assert_eq!(request_counter, 0);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
let comment_id = comment.id;
|
||||
let to_apub = comment.into_apub(&context).await.unwrap();
|
||||
let to_apub = comment.into_json(&context).await.unwrap();
|
||||
assert_json_include!(actual: json, expected: to_apub);
|
||||
|
||||
Comment::delete(context.pool(), comment_id).await.unwrap();
|
||||
@ -283,25 +260,20 @@ pub(crate) mod tests {
|
||||
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")
|
||||
.unwrap();
|
||||
let person_json = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
|
||||
ApubPerson::verify(&person_json, &pleroma_url, &context, &mut 0)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubPerson::from_apub(person_json, &context, &mut 0)
|
||||
ApubPerson::verify(&person_json, &pleroma_url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubPerson::from_json(person_json, &context).await.unwrap();
|
||||
let json = file_to_json_object("assets/pleroma/objects/note.json").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubComment::verify(&json, &pleroma_url, &context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
let comment = ApubComment::from_apub(json, &context, &mut request_counter)
|
||||
ApubComment::verify(&json, &pleroma_url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
let comment = ApubComment::from_json(json, &context).await.unwrap();
|
||||
|
||||
assert_eq!(comment.ap_id, pleroma_url.into());
|
||||
assert_eq!(comment.content.len(), 64);
|
||||
assert!(!comment.local);
|
||||
assert_eq!(request_counter, 0);
|
||||
assert_eq!(context.request_count(), 1);
|
||||
|
||||
Comment::delete(context.pool(), comment.id).await.unwrap();
|
||||
cleanup(data, &context).await;
|
||||
|
@ -1,21 +1,18 @@
|
||||
use crate::{
|
||||
check_apub_id_valid_with_strictness,
|
||||
collections::CommunityContext,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::instance::fetch_instance_actor_for_object,
|
||||
protocol::{
|
||||
objects::{group::Group, Endpoints, LanguageTag},
|
||||
ImageObject,
|
||||
Source,
|
||||
},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
traits::{Actor, ApubObject},
|
||||
config::Data,
|
||||
kinds::actor::GroupType,
|
||||
traits::{Actor, Object},
|
||||
};
|
||||
use activitystreams_kinds::actor::GroupType;
|
||||
use chrono::NaiveDateTime;
|
||||
use itertools::Itertools;
|
||||
use lemmy_api_common::{
|
||||
@ -54,11 +51,10 @@ impl From<Community> for ApubCommunity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubCommunity {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for ApubCommunity {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = Group;
|
||||
type DbType = Community;
|
||||
type Kind = Group;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -66,9 +62,9 @@ impl ApubObject for ApubCommunity {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
Ok(
|
||||
Community::read_from_apub_id(context.pool(), &object_id.into())
|
||||
@ -78,21 +74,21 @@ impl ApubObject for ApubCommunity {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
let form = CommunityUpdateForm::builder().deleted(Some(true)).build();
|
||||
Community::update(context.pool(), self.id, &form).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_apub(self, data: &LemmyContext) -> Result<Group, LemmyError> {
|
||||
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Group, LemmyError> {
|
||||
let community_id = self.id;
|
||||
let langs = CommunityLanguage::read(data.pool(), community_id).await?;
|
||||
let language = LanguageTag::new_multiple(langs, data.pool()).await?;
|
||||
|
||||
let group = Group {
|
||||
kind: GroupType::Group,
|
||||
id: ObjectId::new(self.actor_id()),
|
||||
id: self.id().into(),
|
||||
preferred_username: self.name.clone(),
|
||||
name: Some(self.title.clone()),
|
||||
summary: self.description.as_ref().map(|b| markdown_to_html(b)),
|
||||
@ -103,12 +99,12 @@ impl ApubObject for ApubCommunity {
|
||||
moderators: Some(generate_moderators_url(&self.actor_id)?.into()),
|
||||
featured: Some(generate_featured_url(&self.actor_id)?.into()),
|
||||
inbox: self.inbox_url.clone().into(),
|
||||
outbox: ObjectId::new(generate_outbox_url(&self.actor_id)?),
|
||||
outbox: generate_outbox_url(&self.actor_id)?.into(),
|
||||
followers: self.followers_url.clone().into(),
|
||||
endpoints: self.shared_inbox_url.clone().map(|s| Endpoints {
|
||||
shared_inbox: s.into(),
|
||||
}),
|
||||
public_key: self.get_public_key(),
|
||||
public_key: self.public_key(),
|
||||
language,
|
||||
published: Some(convert_datetime(self.published)),
|
||||
updated: self.updated.map(convert_datetime),
|
||||
@ -122,20 +118,18 @@ impl ApubObject for ApubCommunity {
|
||||
async fn verify(
|
||||
group: &Group,
|
||||
expected_domain: &Url,
|
||||
context: &LemmyContext,
|
||||
_request_counter: &mut i32,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
group.verify(expected_domain, context).await
|
||||
}
|
||||
|
||||
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
async fn from_json(
|
||||
group: Group,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let instance_id = fetch_instance_actor_for_object(&group.id, context, request_counter).await?;
|
||||
let instance_id = fetch_instance_actor_for_object(&group.id, context).await?;
|
||||
|
||||
let form = Group::into_insert_form(group.clone(), instance_id);
|
||||
let languages = LanguageTag::to_language_id_multiple(group.language, context.pool()).await?;
|
||||
@ -144,20 +138,19 @@ impl ApubObject for ApubCommunity {
|
||||
CommunityLanguage::update(context.pool(), languages, community.id).await?;
|
||||
|
||||
let community: ApubCommunity = community.into();
|
||||
let outbox_data = CommunityContext(community.clone(), context.clone());
|
||||
|
||||
// Fetching mods and outbox is not necessary for Lemmy to work, so ignore errors. Besides,
|
||||
// we need to ignore these errors so that tests can work entirely offline.
|
||||
group
|
||||
.outbox
|
||||
.dereference(&outbox_data, local_instance(context).await, request_counter)
|
||||
.dereference(&community, context)
|
||||
.await
|
||||
.map_err(|e| debug!("{}", e))
|
||||
.ok();
|
||||
|
||||
if let Some(moderators) = group.attributed_to.or(group.moderators) {
|
||||
moderators
|
||||
.dereference(&outbox_data, local_instance(context).await, request_counter)
|
||||
.dereference(&community, context)
|
||||
.await
|
||||
.map_err(|e| debug!("{}", e))
|
||||
.ok();
|
||||
@ -168,10 +161,18 @@ impl ApubObject for ApubCommunity {
|
||||
}
|
||||
|
||||
impl Actor for ApubCommunity {
|
||||
fn public_key(&self) -> &str {
|
||||
fn id(&self) -> Url {
|
||||
self.actor_id.inner().clone()
|
||||
}
|
||||
|
||||
fn public_key_pem(&self) -> &str {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
fn private_key_pem(&self) -> Option<String> {
|
||||
self.private_key.clone()
|
||||
}
|
||||
|
||||
fn inbox(&self) -> Url {
|
||||
self.inbox_url.clone().into()
|
||||
}
|
||||
@ -181,15 +182,6 @@ impl Actor for ApubCommunity {
|
||||
}
|
||||
}
|
||||
|
||||
impl ActorType for ApubCommunity {
|
||||
fn actor_id(&self) -> Url {
|
||||
self.actor_id.clone().into()
|
||||
}
|
||||
fn private_key(&self) -> Option<String> {
|
||||
self.private_key.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ApubCommunity {
|
||||
/// For a given community, returns the inboxes of all followers.
|
||||
#[tracing::instrument(skip_all)]
|
||||
@ -230,27 +222,25 @@ pub(crate) mod tests {
|
||||
objects::{instance::tests::parse_lemmy_instance, tests::init_context},
|
||||
protocol::tests::file_to_json_object,
|
||||
};
|
||||
use activitypub_federation::fetch::collection_id::CollectionId;
|
||||
use lemmy_db_schema::{source::site::Site, traits::Crud};
|
||||
use serial_test::serial;
|
||||
|
||||
pub(crate) async fn parse_lemmy_community(context: &LemmyContext) -> ApubCommunity {
|
||||
pub(crate) async fn parse_lemmy_community(context: &Data<LemmyContext>) -> ApubCommunity {
|
||||
// use separate counter so this doesnt affect tests
|
||||
let context2 = context.reset_request_count();
|
||||
let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json").unwrap();
|
||||
// change these links so they dont fetch over the network
|
||||
json.moderators = None;
|
||||
json.attributed_to = None;
|
||||
json.outbox =
|
||||
ObjectId::new(Url::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap());
|
||||
CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap();
|
||||
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubCommunity::verify(&json, &url, context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
let community = ApubCommunity::from_apub(json, context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubCommunity::verify(&json, &url, &context2).await.unwrap();
|
||||
let community = ApubCommunity::from_json(json, &context2).await.unwrap();
|
||||
// this makes one requests to the (intentionally broken) outbox collection
|
||||
assert_eq!(request_counter, 1);
|
||||
assert_eq!(context2.request_count(), 1);
|
||||
community
|
||||
}
|
||||
|
||||
|
@ -1,22 +1,20 @@
|
||||
use crate::{
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::read_from_string_or_source_opt,
|
||||
protocol::{
|
||||
objects::{instance::Instance, LanguageTag},
|
||||
ImageObject,
|
||||
Source,
|
||||
},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
deser::values::MediaTypeHtml,
|
||||
traits::{Actor, ApubObject},
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::actor::ApplicationType,
|
||||
protocol::{values::MediaTypeHtml, verification::verify_domains_match},
|
||||
traits::{Actor, Object},
|
||||
};
|
||||
use activitystreams_kinds::actor::ApplicationType;
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
|
||||
use lemmy_db_schema::{
|
||||
@ -57,11 +55,10 @@ impl From<Site> for ApubSite {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubSite {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for ApubSite {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = Instance;
|
||||
type DbType = Site;
|
||||
type Kind = Instance;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -69,9 +66,9 @@ impl ApubObject for ApubSite {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
data: &Self::DataType,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
Ok(
|
||||
Site::read_from_apub_id(data.pool(), object_id)
|
||||
@ -80,19 +77,19 @@ impl ApubObject for ApubSite {
|
||||
)
|
||||
}
|
||||
|
||||
async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
|
||||
async fn delete(self, _data: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
|
||||
let site_id = self.id;
|
||||
let langs = SiteLanguage::read(data.pool(), site_id).await?;
|
||||
let language = LanguageTag::new_multiple(langs, data.pool()).await?;
|
||||
|
||||
let instance = Instance {
|
||||
kind: ApplicationType::Application,
|
||||
id: ObjectId::new(self.actor_id()),
|
||||
id: self.id().into(),
|
||||
name: self.name.clone(),
|
||||
content: self.sidebar.as_ref().map(|d| markdown_to_html(d)),
|
||||
source: self.sidebar.clone().map(Source::new),
|
||||
@ -102,7 +99,7 @@ impl ApubObject for ApubSite {
|
||||
image: self.banner.clone().map(ImageObject::new),
|
||||
inbox: self.inbox_url.clone().into(),
|
||||
outbox: Url::parse(&format!("{}/site_outbox", self.actor_id))?,
|
||||
public_key: self.get_public_key(),
|
||||
public_key: self.public_key(),
|
||||
language,
|
||||
published: convert_datetime(self.published),
|
||||
updated: self.updated.map(convert_datetime),
|
||||
@ -112,10 +109,9 @@ impl ApubObject for ApubSite {
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
apub: &Self::ApubType,
|
||||
apub: &Self::Kind,
|
||||
expected_domain: &Url,
|
||||
data: &Self::DataType,
|
||||
_request_counter: &mut i32,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = fetch_local_site_data(data.pool()).await?;
|
||||
|
||||
@ -130,11 +126,7 @@ impl ApubObject for ApubSite {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
apub: Self::ApubType,
|
||||
data: &Self::DataType,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<Self, LemmyError> {
|
||||
async fn from_json(apub: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, LemmyError> {
|
||||
let domain = apub.id.inner().domain().expect("group id has domain");
|
||||
let instance = DbInstance::read_or_create(data.pool(), domain.to_string()).await?;
|
||||
|
||||
@ -160,20 +152,19 @@ impl ApubObject for ApubSite {
|
||||
}
|
||||
}
|
||||
|
||||
impl ActorType for ApubSite {
|
||||
fn actor_id(&self) -> Url {
|
||||
self.actor_id.clone().into()
|
||||
}
|
||||
fn private_key(&self) -> Option<String> {
|
||||
self.private_key.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for ApubSite {
|
||||
fn public_key(&self) -> &str {
|
||||
fn id(&self) -> Url {
|
||||
self.actor_id.inner().clone()
|
||||
}
|
||||
|
||||
fn public_key_pem(&self) -> &str {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
fn private_key_pem(&self) -> Option<String> {
|
||||
self.private_key.clone()
|
||||
}
|
||||
|
||||
fn inbox(&self) -> Url {
|
||||
self.inbox_url.clone().into()
|
||||
}
|
||||
@ -182,13 +173,12 @@ impl Actor for ApubSite {
|
||||
/// Try to fetch the instance actor (to make things like instance rules available).
|
||||
pub(in crate::objects) async fn fetch_instance_actor_for_object<T: Into<Url> + Clone>(
|
||||
object_id: &T,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<InstanceId, LemmyError> {
|
||||
let object_id: Url = object_id.clone().into();
|
||||
let instance_id = Site::instance_actor_id_from_url(object_id);
|
||||
let site = ObjectId::<ApubSite>::new(instance_id.clone())
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
let site = ObjectId::<ApubSite>::from(instance_id.clone())
|
||||
.dereference(context)
|
||||
.await;
|
||||
match site {
|
||||
Ok(s) => Ok(s.instance_id),
|
||||
@ -222,17 +212,12 @@ pub(crate) mod tests {
|
||||
use lemmy_db_schema::traits::Crud;
|
||||
use serial_test::serial;
|
||||
|
||||
pub(crate) async fn parse_lemmy_instance(context: &LemmyContext) -> ApubSite {
|
||||
pub(crate) async fn parse_lemmy_instance(context: &Data<LemmyContext>) -> ApubSite {
|
||||
let json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap();
|
||||
let id = Url::parse("https://enterprise.lemmy.ml/").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubSite::verify(&json, &id, context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
let site = ApubSite::from_apub(json, context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(request_counter, 0);
|
||||
ApubSite::verify(&json, &id, context).await.unwrap();
|
||||
let site = ApubSite::from_json(json, context).await.unwrap();
|
||||
assert_eq!(context.request_count(), 0);
|
||||
site
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::protocol::Source;
|
||||
use activitypub_federation::deser::values::MediaTypeMarkdownOrHtml;
|
||||
use activitypub_federation::protocol::values::MediaTypeMarkdownOrHtml;
|
||||
use anyhow::anyhow;
|
||||
use html2md::parse_html;
|
||||
use lemmy_utils::{error::LemmyError, settings::structs::Settings};
|
||||
@ -54,6 +54,7 @@ pub(crate) fn verify_is_remote_object(id: &Url, settings: &Settings) -> Result<(
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use activitypub_federation::config::{Data, FederationConfig};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
@ -87,7 +88,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
// TODO: would be nice if we didnt have to use a full context for tests.
|
||||
pub(crate) async fn init_context() -> LemmyContext {
|
||||
pub(crate) async fn init_context() -> Data<LemmyContext> {
|
||||
async fn x() -> Result<String, LemmyError> {
|
||||
Ok(String::new())
|
||||
}
|
||||
@ -110,13 +111,12 @@ pub(crate) mod tests {
|
||||
let rate_limit_cell = RateLimitCell::new(rate_limit_config).await;
|
||||
|
||||
let chat_server = Arc::new(ChatServer::startup());
|
||||
LemmyContext::create(
|
||||
pool,
|
||||
chat_server,
|
||||
client,
|
||||
settings,
|
||||
secret,
|
||||
rate_limit_cell.clone(),
|
||||
)
|
||||
let context = LemmyContext::create(pool, chat_server, client, secret, rate_limit_cell.clone());
|
||||
let config = FederationConfig::builder()
|
||||
.domain("example.com")
|
||||
.app_data(context)
|
||||
.build()
|
||||
.unwrap();
|
||||
config.to_request_data()
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,11 @@ use crate::{
|
||||
ImageObject,
|
||||
Source,
|
||||
},
|
||||
ActorType,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
traits::{Actor, ApubObject},
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
protocol::verification::verify_domains_match,
|
||||
traits::{Actor, Object},
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::{
|
||||
@ -54,11 +53,10 @@ impl From<DbPerson> for ApubPerson {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubPerson {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for ApubPerson {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = Person;
|
||||
type DbType = DbPerson;
|
||||
type Kind = Person;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -66,9 +64,9 @@ impl ApubObject for ApubPerson {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
Ok(
|
||||
DbPerson::read_from_apub_id(context.pool(), &object_id.into())
|
||||
@ -78,14 +76,14 @@ impl ApubObject for ApubPerson {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
let form = PersonUpdateForm::builder().deleted(Some(true)).build();
|
||||
DbPerson::update(context.pool(), self.id, &form).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_apub(self, _pool: &LemmyContext) -> Result<Person, LemmyError> {
|
||||
async fn into_json(self, _context: &Data<Self::DataType>) -> Result<Person, LemmyError> {
|
||||
let kind = if self.bot_account {
|
||||
UserTypes::Service
|
||||
} else {
|
||||
@ -94,7 +92,7 @@ impl ApubObject for ApubPerson {
|
||||
|
||||
let person = Person {
|
||||
kind,
|
||||
id: ObjectId::new(self.actor_id.clone()),
|
||||
id: self.actor_id.clone().into(),
|
||||
preferred_username: self.name.clone(),
|
||||
name: self.display_name.clone(),
|
||||
summary: self.bio.as_ref().map(|b| markdown_to_html(b)),
|
||||
@ -107,7 +105,7 @@ impl ApubObject for ApubPerson {
|
||||
endpoints: self.shared_inbox_url.clone().map(|s| Endpoints {
|
||||
shared_inbox: s.into(),
|
||||
}),
|
||||
public_key: self.get_public_key(),
|
||||
public_key: self.public_key(),
|
||||
updated: self.updated.map(convert_datetime),
|
||||
inbox: self.inbox_url.clone().into(),
|
||||
};
|
||||
@ -118,8 +116,7 @@ impl ApubObject for ApubPerson {
|
||||
async fn verify(
|
||||
person: &Person,
|
||||
expected_domain: &Url,
|
||||
context: &LemmyContext,
|
||||
_request_counter: &mut i32,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = fetch_local_site_data(context.pool()).await?;
|
||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
|
||||
@ -141,12 +138,11 @@ impl ApubObject for ApubPerson {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
async fn from_json(
|
||||
person: Person,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<ApubPerson, LemmyError> {
|
||||
let instance_id = fetch_instance_actor_for_object(&person.id, context, request_counter).await?;
|
||||
let instance_id = fetch_instance_actor_for_object(&person.id, context).await?;
|
||||
|
||||
let person_form = PersonInsertForm {
|
||||
name: person.preferred_username,
|
||||
@ -177,21 +173,19 @@ impl ApubObject for ApubPerson {
|
||||
}
|
||||
}
|
||||
|
||||
impl ActorType for ApubPerson {
|
||||
fn actor_id(&self) -> Url {
|
||||
self.actor_id.clone().into()
|
||||
}
|
||||
|
||||
fn private_key(&self) -> Option<String> {
|
||||
self.private_key.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for ApubPerson {
|
||||
fn public_key(&self) -> &str {
|
||||
fn id(&self) -> Url {
|
||||
self.actor_id.inner().clone()
|
||||
}
|
||||
|
||||
fn public_key_pem(&self) -> &str {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
fn private_key_pem(&self) -> Option<String> {
|
||||
self.private_key.clone()
|
||||
}
|
||||
|
||||
fn inbox(&self) -> Url {
|
||||
self.inbox_url.clone().into()
|
||||
}
|
||||
@ -211,21 +205,17 @@ pub(crate) mod tests {
|
||||
},
|
||||
protocol::{objects::instance::Instance, tests::file_to_json_object},
|
||||
};
|
||||
use activitypub_federation::fetch::object_id::ObjectId;
|
||||
use lemmy_db_schema::{source::site::Site, traits::Crud};
|
||||
use serial_test::serial;
|
||||
|
||||
pub(crate) async fn parse_lemmy_person(context: &LemmyContext) -> (ApubPerson, ApubSite) {
|
||||
pub(crate) async fn parse_lemmy_person(context: &Data<LemmyContext>) -> (ApubPerson, ApubSite) {
|
||||
let site = parse_lemmy_instance(context).await;
|
||||
let json = file_to_json_object("assets/lemmy/objects/person.json").unwrap();
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubPerson::verify(&json, &url, context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
let person = ApubPerson::from_apub(json, context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(request_counter, 0);
|
||||
ApubPerson::verify(&json, &url, context).await.unwrap();
|
||||
let person = ApubPerson::from_json(json, context).await.unwrap();
|
||||
assert_eq!(context.request_count(), 0);
|
||||
(person, site)
|
||||
}
|
||||
|
||||
@ -249,27 +239,19 @@ pub(crate) mod tests {
|
||||
|
||||
// create and parse a fake pleroma instance actor, to avoid network request during test
|
||||
let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap();
|
||||
let id = Url::parse("https://queer.hacktivis.me/").unwrap();
|
||||
json.id = ObjectId::new(id);
|
||||
let mut request_counter = 0;
|
||||
let site = ApubSite::from_apub(json, &context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
json.id = ObjectId::parse("https://queer.hacktivis.me/").unwrap();
|
||||
let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
||||
ApubSite::verify(&json, &url, &context).await.unwrap();
|
||||
let site = ApubSite::from_json(json, &context).await.unwrap();
|
||||
|
||||
let json = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
|
||||
let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubPerson::verify(&json, &url, &context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
let person = ApubPerson::from_apub(json, &context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubPerson::verify(&json, &url, &context).await.unwrap();
|
||||
let person = ApubPerson::from_json(json, &context).await.unwrap();
|
||||
|
||||
assert_eq!(person.actor_id, url.into());
|
||||
assert_eq!(person.name, "lanodan");
|
||||
assert!(!person.local);
|
||||
assert_eq!(request_counter, 0);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
assert_eq!(person.bio.as_ref().unwrap().len(), 873);
|
||||
|
||||
cleanup((person, site), &context).await;
|
||||
|
@ -2,7 +2,6 @@ use crate::{
|
||||
activities::{verify_is_public, verify_person_in_community},
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{read_from_string_or_source_opt, verify_is_remote_object},
|
||||
protocol::{
|
||||
objects::{
|
||||
@ -15,12 +14,11 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
deser::values::MediaTypeMarkdownOrHtml,
|
||||
traits::ApubObject,
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
kinds::public,
|
||||
protocol::{values::MediaTypeMarkdownOrHtml, verification::verify_domains_match},
|
||||
traits::Object,
|
||||
};
|
||||
use activitystreams_kinds::public;
|
||||
use anyhow::anyhow;
|
||||
use chrono::NaiveDateTime;
|
||||
use html2md::parse_html;
|
||||
@ -69,11 +67,10 @@ impl From<Post> for ApubPost {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubPost {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for ApubPost {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = Page;
|
||||
type DbType = Post;
|
||||
type Kind = Page;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -81,9 +78,9 @@ impl ApubObject for ApubPost {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
Ok(
|
||||
Post::read_from_apub_id(context.pool(), object_id)
|
||||
@ -93,7 +90,7 @@ impl ApubObject for ApubPost {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
if !self.deleted {
|
||||
let form = PostUpdateForm::builder().deleted(Some(true)).build();
|
||||
Post::update(context.pool(), self.id, &form).await?;
|
||||
@ -103,7 +100,7 @@ impl ApubObject for ApubPost {
|
||||
|
||||
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_apub(self, context: &LemmyContext) -> Result<Page, LemmyError> {
|
||||
async fn into_json(self, context: &Data<Self::DataType>) -> Result<Page, LemmyError> {
|
||||
let creator_id = self.creator_id;
|
||||
let creator = Person::read(context.pool(), creator_id).await?;
|
||||
let community_id = self.community_id;
|
||||
@ -112,8 +109,8 @@ impl ApubObject for ApubPost {
|
||||
|
||||
let page = Page {
|
||||
kind: PageType::Page,
|
||||
id: ObjectId::new(self.ap_id.clone()),
|
||||
attributed_to: AttributedTo::Lemmy(ObjectId::new(creator.actor_id)),
|
||||
id: self.ap_id.clone().into(),
|
||||
attributed_to: AttributedTo::Lemmy(creator.actor_id.into()),
|
||||
to: vec![community.actor_id.clone().into(), public()],
|
||||
cc: vec![],
|
||||
name: Some(self.name.clone()),
|
||||
@ -128,7 +125,7 @@ impl ApubObject for ApubPost {
|
||||
language,
|
||||
published: Some(convert_datetime(self.published)),
|
||||
updated: self.updated.map(convert_datetime),
|
||||
audience: Some(ObjectId::new(community.actor_id)),
|
||||
audience: Some(community.actor_id.into()),
|
||||
in_reply_to: None,
|
||||
};
|
||||
Ok(page)
|
||||
@ -138,8 +135,7 @@ impl ApubObject for ApubPost {
|
||||
async fn verify(
|
||||
page: &Page,
|
||||
expected_domain: &Url,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
// We can't verify the domain in case of mod action, because the mod may be on a different
|
||||
// instance from the post author.
|
||||
@ -150,14 +146,14 @@ impl ApubObject for ApubPost {
|
||||
|
||||
let local_site_data = fetch_local_site_data(context.pool()).await?;
|
||||
|
||||
let community = page.community(context, request_counter).await?;
|
||||
let community = page.community(context).await?;
|
||||
check_apub_id_valid_with_strictness(
|
||||
page.id.inner(),
|
||||
community.local,
|
||||
&local_site_data,
|
||||
context.settings(),
|
||||
)?;
|
||||
verify_person_in_community(&page.creator()?, &community, context, request_counter).await?;
|
||||
verify_person_in_community(&page.creator()?, &community, context).await?;
|
||||
|
||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
|
||||
check_slurs_opt(&page.name, slur_regex)?;
|
||||
@ -168,16 +164,9 @@ impl ApubObject for ApubPost {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
page: Page,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubPost, LemmyError> {
|
||||
let creator = page
|
||||
.creator()?
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let community = page.community(context, request_counter).await?;
|
||||
async fn from_json(page: Page, context: &Data<Self::DataType>) -> Result<ApubPost, LemmyError> {
|
||||
let creator = page.creator()?.dereference(context).await?;
|
||||
let community = page.community(context).await?;
|
||||
if community.posting_restricted_to_mods {
|
||||
is_mod_or_admin(context.pool(), creator.id, community.id).await?;
|
||||
}
|
||||
@ -257,9 +246,7 @@ impl ApubObject for ApubPost {
|
||||
.build()
|
||||
};
|
||||
// read existing, local post if any (for generating mod log)
|
||||
let old_post = ObjectId::<ApubPost>::new(page.id.clone())
|
||||
.dereference_local(context)
|
||||
.await;
|
||||
let old_post = page.id.dereference_local(context).await;
|
||||
|
||||
let post = Post::create(context.pool(), &form).await?;
|
||||
|
||||
@ -310,13 +297,8 @@ mod tests {
|
||||
|
||||
let json = file_to_json_object("assets/lemmy/objects/page.json").unwrap();
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubPost::verify(&json, &url, &context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
let post = ApubPost::from_apub(json, &context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubPost::verify(&json, &url, &context).await.unwrap();
|
||||
let post = ApubPost::from_json(json, &context).await.unwrap();
|
||||
|
||||
assert_eq!(post.ap_id, url.into());
|
||||
assert_eq!(post.name, "Post title");
|
||||
@ -324,7 +306,7 @@ mod tests {
|
||||
assert_eq!(post.body.as_ref().unwrap().len(), 45);
|
||||
assert!(!post.locked);
|
||||
assert!(post.featured_community);
|
||||
assert_eq!(request_counter, 0);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
Post::delete(context.pool(), post.id).await.unwrap();
|
||||
Person::delete(context.pool(), person.id).await.unwrap();
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::read_from_string_or_source,
|
||||
protocol::{
|
||||
objects::chat_message::{ChatMessage, ChatMessageType},
|
||||
@ -9,10 +8,9 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
deser::values::MediaTypeHtml,
|
||||
traits::ApubObject,
|
||||
utils::verify_domains_match,
|
||||
config::Data,
|
||||
protocol::{values::MediaTypeHtml, verification::verify_domains_match},
|
||||
traits::Object,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::check_person_block};
|
||||
@ -46,11 +44,10 @@ impl From<PrivateMessage> for ApubPrivateMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObject for ApubPrivateMessage {
|
||||
#[async_trait::async_trait]
|
||||
impl Object for ApubPrivateMessage {
|
||||
type DataType = LemmyContext;
|
||||
type ApubType = ChatMessage;
|
||||
type DbType = PrivateMessage;
|
||||
type Kind = ChatMessage;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||
@ -58,9 +55,9 @@ impl ApubObject for ApubPrivateMessage {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn read_from_apub_id(
|
||||
async fn read_from_id(
|
||||
object_id: Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, LemmyError> {
|
||||
Ok(
|
||||
PrivateMessage::read_from_apub_id(context.pool(), object_id)
|
||||
@ -69,13 +66,13 @@ impl ApubObject for ApubPrivateMessage {
|
||||
)
|
||||
}
|
||||
|
||||
async fn delete(self, _context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
async fn delete(self, _context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
// do nothing, because pm can't be fetched over http
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn into_apub(self, context: &LemmyContext) -> Result<ChatMessage, LemmyError> {
|
||||
async fn into_json(self, context: &Data<Self::DataType>) -> Result<ChatMessage, LemmyError> {
|
||||
let creator_id = self.creator_id;
|
||||
let creator = Person::read(context.pool(), creator_id).await?;
|
||||
|
||||
@ -84,9 +81,9 @@ impl ApubObject for ApubPrivateMessage {
|
||||
|
||||
let note = ChatMessage {
|
||||
r#type: ChatMessageType::ChatMessage,
|
||||
id: ObjectId::new(self.ap_id.clone()),
|
||||
attributed_to: ObjectId::new(creator.actor_id),
|
||||
to: [ObjectId::new(recipient.actor_id)],
|
||||
id: self.ap_id.clone().into(),
|
||||
attributed_to: creator.actor_id.into(),
|
||||
to: [recipient.actor_id.into()],
|
||||
content: markdown_to_html(&self.content),
|
||||
media_type: Some(MediaTypeHtml::Html),
|
||||
source: Some(Source::new(self.content.clone())),
|
||||
@ -100,8 +97,7 @@ impl ApubObject for ApubPrivateMessage {
|
||||
async fn verify(
|
||||
note: &ChatMessage,
|
||||
expected_domain: &Url,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_domains_match(note.id.inner(), expected_domain)?;
|
||||
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
|
||||
@ -114,10 +110,7 @@ impl ApubObject for ApubPrivateMessage {
|
||||
&local_site_data,
|
||||
context.settings(),
|
||||
)?;
|
||||
let person = note
|
||||
.attributed_to
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let person = note.attributed_to.dereference(context).await?;
|
||||
if person.banned {
|
||||
return Err(LemmyError::from_message("Person is banned from site"));
|
||||
}
|
||||
@ -125,18 +118,12 @@ impl ApubObject for ApubPrivateMessage {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn from_apub(
|
||||
async fn from_json(
|
||||
note: ChatMessage,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<Self::DataType>,
|
||||
) -> Result<ApubPrivateMessage, LemmyError> {
|
||||
let creator = note
|
||||
.attributed_to
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let recipient = note.to[0]
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
let creator = note.attributed_to.dereference(context).await?;
|
||||
let recipient = note.to[0].dereference(context).await?;
|
||||
check_person_block(creator.id, recipient.id, context.pool()).await?;
|
||||
|
||||
let form = PrivateMessageInsertForm {
|
||||
@ -172,28 +159,29 @@ mod tests {
|
||||
|
||||
async fn prepare_comment_test(
|
||||
url: &Url,
|
||||
context: &LemmyContext,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> (ApubPerson, ApubPerson, ApubSite) {
|
||||
let context2 = context.reset_request_count();
|
||||
let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json").unwrap();
|
||||
let site = parse_lemmy_instance(context).await;
|
||||
ApubPerson::verify(&lemmy_person, url, context, &mut 0)
|
||||
let site = parse_lemmy_instance(&context2).await;
|
||||
ApubPerson::verify(&lemmy_person, url, &context2)
|
||||
.await
|
||||
.unwrap();
|
||||
let person1 = ApubPerson::from_apub(lemmy_person, context, &mut 0)
|
||||
let person1 = ApubPerson::from_json(lemmy_person, &context2)
|
||||
.await
|
||||
.unwrap();
|
||||
let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
|
||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
||||
ApubPerson::verify(&pleroma_person, &pleroma_url, context, &mut 0)
|
||||
ApubPerson::verify(&pleroma_person, &pleroma_url, &context2)
|
||||
.await
|
||||
.unwrap();
|
||||
let person2 = ApubPerson::from_apub(pleroma_person, context, &mut 0)
|
||||
let person2 = ApubPerson::from_json(pleroma_person, &context2)
|
||||
.await
|
||||
.unwrap();
|
||||
(person1, person2, site)
|
||||
}
|
||||
|
||||
async fn cleanup(data: (ApubPerson, ApubPerson, ApubSite), context: &LemmyContext) {
|
||||
async fn cleanup(data: (ApubPerson, ApubPerson, ApubSite), context: &Data<LemmyContext>) {
|
||||
Person::delete(context.pool(), data.0.id).await.unwrap();
|
||||
Person::delete(context.pool(), data.1.id).await.unwrap();
|
||||
Site::delete(context.pool(), data.2.id).await.unwrap();
|
||||
@ -206,20 +194,19 @@ mod tests {
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap();
|
||||
let data = prepare_comment_test(&url, &context).await;
|
||||
let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubPrivateMessage::verify(&json, &url, &context, &mut request_counter)
|
||||
ApubPrivateMessage::verify(&json, &url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
let pm = ApubPrivateMessage::from_apub(json.clone(), &context, &mut request_counter)
|
||||
let pm = ApubPrivateMessage::from_json(json.clone(), &context)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(pm.ap_id.clone(), url.into());
|
||||
assert_eq!(pm.content.len(), 20);
|
||||
assert_eq!(request_counter, 0);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
let pm_id = pm.id;
|
||||
let to_apub = pm.into_apub(&context).await.unwrap();
|
||||
let to_apub = pm.into_json(&context).await.unwrap();
|
||||
assert_json_include!(actual: json, expected: to_apub);
|
||||
|
||||
PrivateMessage::delete(context.pool(), pm_id).await.unwrap();
|
||||
@ -234,17 +221,14 @@ mod tests {
|
||||
let data = prepare_comment_test(&url, &context).await;
|
||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2").unwrap();
|
||||
let json = file_to_json_object("assets/pleroma/objects/chat_message.json").unwrap();
|
||||
let mut request_counter = 0;
|
||||
ApubPrivateMessage::verify(&json, &pleroma_url, &context, &mut request_counter)
|
||||
.await
|
||||
.unwrap();
|
||||
let pm = ApubPrivateMessage::from_apub(json, &context, &mut request_counter)
|
||||
ApubPrivateMessage::verify(&json, &pleroma_url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
let pm = ApubPrivateMessage::from_json(json, &context).await.unwrap();
|
||||
|
||||
assert_eq!(pm.ap_id, pleroma_url.into());
|
||||
assert_eq!(pm.content.len(), 3);
|
||||
assert_eq!(request_counter, 0);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
PrivateMessage::delete(context.pool(), pm.id).await.unwrap();
|
||||
cleanup(data, &context).await;
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::{
|
||||
activities::{block::SiteOrCommunity, verify_community_matches},
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::InCommunity,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::BlockType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::BlockType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
@ -38,17 +41,10 @@ pub struct BlockUser {
|
||||
pub(crate) expires: Option<DateTime<FixedOffset>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for BlockUser {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let target = self
|
||||
.target
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let target = self.target.dereference(context).await?;
|
||||
let community = match target {
|
||||
SiteOrCommunity::Community(c) => c,
|
||||
SiteOrCommunity::Site(_) => return Err(anyhow!("activity is not in community").into()),
|
||||
|
@ -3,8 +3,12 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{activities::block::block_user::BlockUser, InCommunity},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::UndoType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -27,14 +31,10 @@ pub struct UndoBlockUser {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for UndoBlockUser {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context, request_counter).await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
use crate::{objects::community::ApubCommunity, protocol::IdOrNestedObject};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::AnnounceType;
|
||||
use activitypub_federation::{
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::AnnounceType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use url::Url;
|
||||
|
@ -3,8 +3,12 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::InCommunity,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::AddType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::AddType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::source::community::Community;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -27,13 +31,9 @@ pub struct CollectionAdd {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for CollectionAdd {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let (community, _) =
|
||||
Community::get_by_collection_url(context.pool(), &self.clone().target.into()).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
|
@ -3,8 +3,12 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::InCommunity,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::RemoveType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::RemoveType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::source::community::Community;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -17,7 +21,7 @@ pub struct CollectionRemove {
|
||||
pub(crate) actor: ObjectId<ApubPerson>,
|
||||
#[serde(deserialize_with = "deserialize_one_or_many")]
|
||||
pub(crate) to: Vec<Url>,
|
||||
pub(crate) object: ObjectId<ApubPerson>,
|
||||
pub(crate) object: Url,
|
||||
#[serde(deserialize_with = "deserialize_one_or_many")]
|
||||
pub(crate) cc: Vec<Url>,
|
||||
#[serde(rename = "type")]
|
||||
@ -27,13 +31,9 @@ pub struct CollectionRemove {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for CollectionRemove {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let (community, _) =
|
||||
Community::get_by_collection_url(context.pool(), &self.clone().target.into()).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::{
|
||||
activities::verify_community_matches,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||
protocol::InCommunity,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::UndoType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{source::community::Community, traits::Crud};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -48,17 +51,10 @@ pub struct UndoLockPage {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for LockPage {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let post = self
|
||||
.object
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let post = self.object.dereference(context).await?;
|
||||
let community = Community::read(context.pool(), post.community_id).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
@ -67,14 +63,10 @@ impl InCommunity for LockPage {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for UndoLockPage {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context, request_counter).await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
use crate::{
|
||||
activities::verify_community_matches,
|
||||
fetcher::post_or_comment::PostOrComment,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::InCommunity,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
|
||||
use activitystreams_kinds::activity::FlagType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::FlagType,
|
||||
protocol::helpers::deserialize_one,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -26,16 +29,10 @@ pub struct Report {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for Report {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.to[0]
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.to[0].dereference(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::{
|
||||
activities::verify_community_matches,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{objects::group::Group, InCommunity},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::UpdateType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::UpdateType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -29,16 +32,10 @@ pub struct UpdateCommunity {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for UpdateCommunity {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let community: ApubCommunity = ObjectId::new(self.object.id.clone())
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community: ApubCommunity = self.object.id.clone().dereference(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
|
||||
use activitypub_federation::{fetch::object_id::ObjectId, protocol::helpers::deserialize_one};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -4,7 +4,11 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{activities::CreateOrUpdateType, objects::note::Note, InCommunity},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{source::community::Community, traits::Crud};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -28,14 +32,10 @@ pub struct CreateOrUpdateNote {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for CreateOrUpdateNote {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let post = self.object.get_parents(context, request_counter).await?.0;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let post = self.object.get_parents(context).await?.0;
|
||||
let community = Community::read(context.pool(), post.community_id).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
|
@ -3,7 +3,11 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{activities::CreateOrUpdateType, objects::page::Page, InCommunity},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -24,14 +28,10 @@ pub struct CreateOrUpdatePage {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for CreateOrUpdatePage {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context, request_counter).await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
|
@ -3,8 +3,12 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{objects::tombstone::Tombstone, IdOrNestedObject, InCommunity},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::DeleteType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::DeleteType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
@ -38,13 +42,9 @@ pub struct Delete {
|
||||
pub(crate) summary: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for Delete {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? {
|
||||
DeletableObjects::Community(c) => c.id,
|
||||
DeletableObjects::Comment(c) => {
|
||||
|
@ -1,6 +1,9 @@
|
||||
use crate::objects::person::ApubPerson;
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::DeleteType;
|
||||
use activitypub_federation::{
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::DeleteType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use url::Url;
|
||||
|
@ -3,8 +3,12 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{activities::deletion::delete::Delete, InCommunity},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::activity::UndoType,
|
||||
protocol::helpers::deserialize_one_or_many,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -29,14 +33,10 @@ pub struct UndoDelete {
|
||||
pub(crate) cc: Vec<Url>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for UndoDelete {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context, request_counter).await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{objects::community::ApubCommunity, protocol::activities::following::follow::Follow};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::activity::AcceptType;
|
||||
use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::AcceptType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{fetcher::user_or_community::UserOrCommunity, objects::person::ApubPerson};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::activity::FollowType;
|
||||
use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::FollowType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{objects::person::ApubPerson, protocol::activities::following::follow::Follow};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::UndoType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -3,8 +3,7 @@ use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{activities::voting::vote::Vote, InCommunity},
|
||||
};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use activitypub_federation::{config::Data, fetch::object_id::ObjectId, kinds::activity::UndoType};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -21,14 +20,10 @@ pub struct UndoVote {
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for UndoVote {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context, request_counter).await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::{
|
||||
activities::verify_community_matches,
|
||||
fetcher::post_or_comment::PostOrComment,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::InCommunity,
|
||||
};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -51,19 +50,14 @@ impl From<&VoteType> for i16 {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for Vote {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let local_instance = local_instance(context).await;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = self
|
||||
.object
|
||||
.dereference(context, local_instance, request_counter)
|
||||
.dereference(context)
|
||||
.await?
|
||||
.community(context, request_counter)
|
||||
.community(context)
|
||||
.await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use activitystreams_kinds::collection::OrderedCollectionType;
|
||||
use activitypub_federation::kinds::collection::OrderedCollectionType;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::protocol::objects::page::Page;
|
||||
use activitystreams_kinds::collection::OrderedCollectionType;
|
||||
use activitypub_federation::kinds::collection::OrderedCollectionType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use activitystreams_kinds::collection::CollectionType;
|
||||
use activitypub_federation::kinds::collection::CollectionType;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::generate_followers_url};
|
||||
use lemmy_db_schema::source::community::Community;
|
||||
use lemmy_db_views_actor::structs::CommunityFollowerView;
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::objects::person::ApubPerson;
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::collection::OrderedCollectionType;
|
||||
use activitypub_federation::{
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::collection::OrderedCollectionType,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::protocol::activities::community::announce::AnnounceActivity;
|
||||
use activitystreams_kinds::collection::OrderedCollectionType;
|
||||
use activitypub_federation::kinds::collection::OrderedCollectionType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
use crate::{local_instance, objects::community::ApubCommunity};
|
||||
use activitypub_federation::{deser::values::MediaTypeMarkdown, utils::fetch_object_http};
|
||||
use activitystreams_kinds::object::ImageType;
|
||||
use crate::objects::community::ApubCommunity;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
fetch::fetch_object_http,
|
||||
kinds::object::ImageType,
|
||||
protocol::values::MediaTypeMarkdown,
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::newtypes::DbUrl;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -60,41 +64,32 @@ pub(crate) enum IdOrNestedObject<Kind: Id> {
|
||||
NestedObject(Kind),
|
||||
}
|
||||
|
||||
impl<Kind: Id + DeserializeOwned> IdOrNestedObject<Kind> {
|
||||
impl<Kind: Id + DeserializeOwned + Send> IdOrNestedObject<Kind> {
|
||||
pub(crate) fn id(&self) -> &Url {
|
||||
match self {
|
||||
IdOrNestedObject::Id(i) => i,
|
||||
IdOrNestedObject::NestedObject(n) => n.object_id(),
|
||||
}
|
||||
}
|
||||
pub(crate) async fn object(
|
||||
self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<Kind, LemmyError> {
|
||||
pub(crate) async fn object(self, context: &Data<LemmyContext>) -> Result<Kind, LemmyError> {
|
||||
match self {
|
||||
IdOrNestedObject::Id(i) => {
|
||||
Ok(fetch_object_http(&i, local_instance(context).await, request_counter).await?)
|
||||
}
|
||||
// TODO: move IdOrNestedObject struct to library and make fetch_object_http private
|
||||
IdOrNestedObject::Id(i) => Ok(fetch_object_http(&i, context).await?),
|
||||
IdOrNestedObject::NestedObject(o) => Ok(o),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
pub trait InCommunity {
|
||||
// TODO: after we use audience field and remove backwards compat, it should be possible to change
|
||||
// this to simply `fn community(&self) -> Result<ObjectId<ApubCommunity>, LemmyError>`
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError>;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use activitypub_federation::deser::context::WithContext;
|
||||
use activitypub_federation::protocol::context::WithContext;
|
||||
use assert_json_diff::assert_json_include;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
@ -3,8 +3,8 @@ use crate::{
|
||||
protocol::Source,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
deser::{
|
||||
fetch::object_id::ObjectId,
|
||||
protocol::{
|
||||
helpers::{deserialize_one, deserialize_skip_error},
|
||||
values::MediaTypeHtml,
|
||||
},
|
||||
|
@ -14,11 +14,14 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::{object_id::ObjectId, signatures::PublicKey},
|
||||
deser::helpers::deserialize_skip_error,
|
||||
utils::verify_domains_match,
|
||||
fetch::{collection_id::CollectionId, object_id::ObjectId},
|
||||
kinds::actor::GroupType,
|
||||
protocol::{
|
||||
helpers::deserialize_skip_error,
|
||||
public_key::PublicKey,
|
||||
verification::verify_domains_match,
|
||||
},
|
||||
};
|
||||
use activitystreams_kinds::actor::GroupType;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
|
||||
use lemmy_db_schema::{
|
||||
@ -59,14 +62,14 @@ pub struct Group {
|
||||
// lemmy extension
|
||||
pub(crate) sensitive: Option<bool>,
|
||||
// deprecated, use attributed_to instead
|
||||
pub(crate) moderators: Option<ObjectId<ApubCommunityModerators>>,
|
||||
pub(crate) moderators: Option<CollectionId<ApubCommunityModerators>>,
|
||||
#[serde(deserialize_with = "deserialize_skip_error", default)]
|
||||
pub(crate) attributed_to: Option<ObjectId<ApubCommunityModerators>>,
|
||||
pub(crate) attributed_to: Option<CollectionId<ApubCommunityModerators>>,
|
||||
// lemmy extension
|
||||
pub(crate) posting_restricted_to_mods: Option<bool>,
|
||||
pub(crate) outbox: ObjectId<ApubCommunityOutbox>,
|
||||
pub(crate) outbox: CollectionId<ApubCommunityOutbox>,
|
||||
pub(crate) endpoints: Option<Endpoints>,
|
||||
pub(crate) featured: Option<ObjectId<ApubCommunityFeatured>>,
|
||||
pub(crate) featured: Option<CollectionId<ApubCommunityFeatured>>,
|
||||
#[serde(default)]
|
||||
pub(crate) language: Vec<LanguageTag>,
|
||||
pub(crate) published: Option<DateTime<FixedOffset>>,
|
||||
|
@ -3,10 +3,10 @@ use crate::{
|
||||
protocol::{objects::LanguageTag, ImageObject, Source},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::{object_id::ObjectId, signatures::PublicKey},
|
||||
deser::{helpers::deserialize_skip_error, values::MediaTypeHtml},
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::actor::ApplicationType,
|
||||
protocol::{helpers::deserialize_skip_error, public_key::PublicKey, values::MediaTypeHtml},
|
||||
};
|
||||
use activitystreams_kinds::actor::ApplicationType;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
|
@ -1,19 +1,19 @@
|
||||
use crate::{
|
||||
activities::verify_community_matches,
|
||||
fetcher::post_or_comment::PostOrComment,
|
||||
local_instance,
|
||||
mentions::MentionOrValue,
|
||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||
protocol::{objects::LanguageTag, InCommunity, Source},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
deser::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::object::NoteType,
|
||||
protocol::{
|
||||
helpers::{deserialize_one_or_many, deserialize_skip_error},
|
||||
values::MediaTypeMarkdownOrHtml,
|
||||
},
|
||||
};
|
||||
use activitystreams_kinds::object::NoteType;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
@ -56,16 +56,10 @@ pub struct Note {
|
||||
impl Note {
|
||||
pub(crate) async fn get_parents(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(ApubPost, Option<ApubComment>), LemmyError> {
|
||||
// Fetch parent comment chain in a box, otherwise it can cause a stack overflow.
|
||||
let parent = Box::pin(
|
||||
self
|
||||
.in_reply_to
|
||||
.dereference(context, local_instance(context).await, request_counter)
|
||||
.await?,
|
||||
);
|
||||
let parent = Box::pin(self.in_reply_to.dereference(context).await?);
|
||||
match parent.deref() {
|
||||
PostOrComment::Post(p) => Ok((p.clone(), None)),
|
||||
PostOrComment::Comment(c) => {
|
||||
@ -77,14 +71,10 @@ impl Note {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for Note {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let (post, _) = self.get_parents(context, request_counter).await?;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let (post, _) = self.get_parents(context).await?;
|
||||
let community = Community::read(context.pool(), post.community_id).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::{
|
||||
activities::verify_community_matches,
|
||||
fetcher::user_or_community::{PersonOrGroupType, UserOrCommunity},
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||
protocol::{objects::LanguageTag, ImageObject, InCommunity, Source},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
deser::{
|
||||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::{
|
||||
link::LinkType,
|
||||
object::{DocumentType, ImageType},
|
||||
},
|
||||
protocol::{
|
||||
helpers::{deserialize_one_or_many, deserialize_skip_error},
|
||||
values::MediaTypeMarkdownOrHtml,
|
||||
},
|
||||
traits::{ActivityHandler, ApubObject},
|
||||
};
|
||||
use activitystreams_kinds::{
|
||||
link::LinkType,
|
||||
object::{DocumentType, ImageType},
|
||||
traits::{ActivityHandler, Object},
|
||||
};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use itertools::Itertools;
|
||||
@ -136,10 +135,11 @@ impl Page {
|
||||
/// the current value, it is a mod action and needs to be verified as such.
|
||||
///
|
||||
/// Both stickied and locked need to be false on a newly created post (verified in [[CreatePost]].
|
||||
pub(crate) async fn is_mod_action(&self, context: &LemmyContext) -> Result<bool, LemmyError> {
|
||||
let old_post = ObjectId::<ApubPost>::new(self.id.clone())
|
||||
.dereference_local(context)
|
||||
.await;
|
||||
pub(crate) async fn is_mod_action(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<bool, LemmyError> {
|
||||
let old_post = self.id.clone().dereference_local(context).await;
|
||||
|
||||
let featured_changed = Page::is_featured_changed(&old_post, &self.stickied);
|
||||
let locked_changed = Page::is_locked_changed(&old_post, &self.comments_enabled);
|
||||
@ -178,7 +178,7 @@ impl Page {
|
||||
AttributedTo::Peertube(p) => p
|
||||
.iter()
|
||||
.find(|a| a.kind == PersonOrGroupType::Person)
|
||||
.map(|a| ObjectId::<ApubPerson>::new(a.id.clone().into_inner()))
|
||||
.map(|a| ObjectId::<ApubPerson>::from(a.id.clone().into_inner()))
|
||||
.ok_or_else(|| LemmyError::from_message("page does not specify creator person")),
|
||||
}
|
||||
}
|
||||
@ -194,7 +194,7 @@ impl Attachment {
|
||||
}
|
||||
|
||||
// Used for community outbox, so that it can be compatible with Pleroma/Mastodon.
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for Page {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
@ -204,38 +204,25 @@ impl ActivityHandler for Page {
|
||||
fn actor(&self) -> &Url {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn verify(
|
||||
&self,
|
||||
data: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
ApubPost::verify(self, self.id.inner(), data, request_counter).await
|
||||
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
ApubPost::verify(self, self.id.inner(), data).await
|
||||
}
|
||||
async fn receive(
|
||||
self,
|
||||
data: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
ApubPost::from_apub(self, data, request_counter).await?;
|
||||
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
ApubPost::from_json(self, data).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for Page {
|
||||
async fn community(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let instance = local_instance(context).await;
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
|
||||
let community = match &self.attributed_to {
|
||||
AttributedTo::Lemmy(_) => {
|
||||
let mut iter = self.to.iter().merge(self.cc.iter());
|
||||
loop {
|
||||
if let Some(cid) = iter.next() {
|
||||
let cid = ObjectId::new(cid.clone());
|
||||
if let Ok(c) = cid.dereference(context, instance, request_counter).await {
|
||||
let cid = ObjectId::from(cid.clone());
|
||||
if let Ok(c) = cid.dereference(context).await {
|
||||
break c;
|
||||
}
|
||||
} else {
|
||||
@ -246,9 +233,9 @@ impl InCommunity for Page {
|
||||
AttributedTo::Peertube(p) => {
|
||||
p.iter()
|
||||
.find(|a| a.kind == PersonOrGroupType::Group)
|
||||
.map(|a| ObjectId::<ApubCommunity>::new(a.id.clone().into_inner()))
|
||||
.map(|a| ObjectId::<ApubCommunity>::from(a.id.clone().into_inner()))
|
||||
.ok_or_else(|| LemmyError::from_message("page does not specify group"))?
|
||||
.dereference(context, instance, request_counter)
|
||||
.dereference(context)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
|
@ -3,8 +3,8 @@ use crate::{
|
||||
protocol::{objects::Endpoints, ImageObject, Source},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::{object_id::ObjectId, signatures::PublicKey},
|
||||
deser::helpers::deserialize_skip_error,
|
||||
fetch::object_id::ObjectId,
|
||||
protocol::{helpers::deserialize_skip_error, public_key::PublicKey},
|
||||
};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::protocol::Id;
|
||||
use activitystreams_kinds::object::TombstoneType;
|
||||
use activitypub_federation::kinds::object::TombstoneType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use url::Url;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user