fix post/comment create/update, rework voting

This commit is contained in:
Felix Ableitner 2021-07-10 04:11:21 +02:00
parent ddf480d6e2
commit 99f55a4627
31 changed files with 462 additions and 523 deletions

View file

@ -1,4 +1,10 @@
use crate::{fetcher::fetch::fetch_remote_object, objects::FromApub, NoteExt, PageExt}; use crate::{
fetcher::fetch::fetch_remote_object,
objects::FromApub,
NoteExt,
PageExt,
PostOrComment,
};
use anyhow::anyhow; use anyhow::anyhow;
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
@ -89,3 +95,19 @@ pub async fn get_or_fetch_and_insert_comment(
Err(e) => Err(e.into()), Err(e) => Err(e.into()),
} }
} }
pub async fn get_or_fetch_and_insert_post_or_comment(
ap_id: &Url,
context: &LemmyContext,
recursion_counter: &mut i32,
) -> Result<PostOrComment, LemmyError> {
Ok(
match get_or_fetch_and_insert_post(ap_id, context, recursion_counter).await {
Ok(p) => PostOrComment::Post(Box::new(p)),
Err(_) => {
let c = get_or_fetch_and_insert_comment(ap_id, context, recursion_counter).await?;
PostOrComment::Comment(Box::new(c))
}
},
)
}

View file

@ -123,19 +123,9 @@ impl FromApub for Comment {
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
check_object_for_community_or_site_ban(note, post.community_id, context, request_counter) check_object_for_community_or_site_ban(note, post.community_id, context, request_counter)
.await?; .await?;
if post.locked {
// This is not very efficient because a comment gets inserted just to be deleted right
// afterwards, but it seems to be the easiest way to implement it.
blocking(context.pool(), move |conn| {
Comment::delete(conn, comment.id)
})
.await??;
Err(anyhow!("Post is locked").into())
} else {
Ok(comment) Ok(comment)
} }
} }
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApubToForm<NoteExt> for CommentForm { impl FromApubToForm<NoteExt> for CommentForm {
@ -174,6 +164,9 @@ impl FromApubToForm<NoteExt> for CommentForm {
request_counter, request_counter,
)) ))
.await?; .await?;
if post.locked {
return Err(anyhow!("Post is locked").into());
}
// The 2nd item, if it exists, is the parent comment apub_id // The 2nd item, if it exists, is the parent comment apub_id
// For deeply nested comments, FromApub automatically gets called recursively // For deeply nested comments, FromApub automatically gets called recursively

View file

@ -1,7 +1,16 @@
use crate::activities::comment::{get_notif_recipients, send_websocket_message}; use crate::activities::{
comment::{get_notif_recipients, send_websocket_message},
verify_activity,
verify_person_in_community,
};
use activitystreams::{activity::kind::CreateType, base::BaseExt}; use activitystreams::{activity::kind::CreateType, base::BaseExt};
use lemmy_apub::{check_is_apub_id_valid, objects::FromApub, NoteExt}; use lemmy_apub::{objects::FromApub, NoteExt};
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; use lemmy_apub_lib::{
verify_domains_match_opt,
ActivityCommonFields,
ActivityHandlerNew,
PublicUrl,
};
use lemmy_db_schema::source::comment::Comment; use lemmy_db_schema::source::comment::Comment;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{LemmyContext, UserOperationCrud}; use lemmy_websocket::{LemmyContext, UserOperationCrud};
@ -21,10 +30,21 @@ pub struct CreateComment {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for CreateComment { impl ActivityHandlerNew for CreateComment {
async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; &self,
self.object.id(self.common.actor.as_str())?; context: &LemmyContext,
check_is_apub_id_valid(&self.common.actor, false) request_counter: &mut i32,
) -> Result<(), LemmyError> {
dbg!("1");
verify_activity(self.common())?;
dbg!("2");
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
dbg!("3");
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
dbg!("4");
// TODO: should add a check that the correct community is in cc (probably needs changes to
// comment deserialization)
Ok(())
} }
async fn receive( async fn receive(
@ -32,6 +52,7 @@ impl ActivityHandlerNew for CreateComment {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
dbg!("5");
let comment = Comment::from_apub( let comment = Comment::from_apub(
&self.object, &self.object,
context, context,
@ -40,6 +61,7 @@ impl ActivityHandlerNew for CreateComment {
false, false,
) )
.await?; .await?;
dbg!("6");
let recipients = let recipients =
get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?; get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?;
send_websocket_message( send_websocket_message(

View file

@ -1,46 +0,0 @@
use crate::activities::comment::like_or_dislike_comment;
use activitystreams::activity::kind::LikeType;
use lemmy_apub::check_is_apub_id_valid;
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LikeComment {
to: PublicUrl,
pub(in crate::activities::comment) object: Url,
cc: [Url; 1],
#[serde(rename = "type")]
kind: LikeType,
#[serde(flatten)]
common: ActivityCommonFields,
}
#[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for LikeComment {
async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
check_is_apub_id_valid(&self.common.actor, false)
}
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
like_or_dislike_comment(
1,
&self.common.actor,
&self.object,
context,
request_counter,
)
.await
}
fn common(&self) -> &ActivityCommonFields {
&self.common
}
}

View file

@ -1,30 +1,20 @@
use lemmy_api_common::{blocking, comment::CommentResponse, send_local_notifs}; use lemmy_api_common::{blocking, comment::CommentResponse, send_local_notifs};
use lemmy_apub::fetcher::{ use lemmy_apub::fetcher::person::get_or_fetch_and_upsert_person;
objects::get_or_fetch_and_insert_comment, use lemmy_db_queries::Crud;
person::get_or_fetch_and_upsert_person,
};
use lemmy_db_queries::{Crud, Likeable};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{comment::Comment, post::Post},
comment::{Comment, CommentLike, CommentLikeForm},
post::Post,
},
CommentId, CommentId,
LocalUserId, LocalUserId,
}; };
use lemmy_db_views::comment_view::CommentView; use lemmy_db_views::comment_view::CommentView;
use lemmy_utils::{utils::scrape_text_for_mentions, LemmyError}; use lemmy_utils::{utils::scrape_text_for_mentions, LemmyError};
use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendComment, LemmyContext};
use url::Url; use url::Url;
pub mod create; pub mod create;
pub mod delete; pub mod delete;
pub mod dislike;
pub mod like;
pub mod remove; pub mod remove;
pub mod undo_delete; pub mod undo_delete;
pub mod undo_dislike;
pub mod undo_like;
pub mod undo_remove; pub mod undo_remove;
pub mod update; pub mod update;
@ -49,7 +39,9 @@ async fn get_notif_recipients(
// TODO: in many call sites we are setting an empty vec for recipient_ids, we should get the actual // TODO: in many call sites we are setting an empty vec for recipient_ids, we should get the actual
// recipient actors from somewhere // recipient actors from somewhere
async fn send_websocket_message<OP: ToString + Send + lemmy_websocket::OperationType + 'static>( pub(crate) async fn send_websocket_message<
OP: ToString + Send + lemmy_websocket::OperationType + 'static,
>(
comment_id: CommentId, comment_id: CommentId,
recipient_ids: Vec<LocalUserId>, recipient_ids: Vec<LocalUserId>,
op: OP, op: OP,
@ -75,61 +67,3 @@ async fn send_websocket_message<OP: ToString + Send + lemmy_websocket::Operation
Ok(()) Ok(())
} }
async fn like_or_dislike_comment(
score: i16,
actor: &Url,
object: &Url,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?;
let comment = get_or_fetch_and_insert_comment(object, context, request_counter).await?;
let comment_id = comment.id;
let like_form = CommentLikeForm {
comment_id,
post_id: comment.post_id,
person_id: actor.id,
score,
};
let person_id = actor.id;
blocking(context.pool(), move |conn| {
CommentLike::remove(conn, person_id, comment_id)?;
CommentLike::like(conn, &like_form)
})
.await??;
send_websocket_message(
comment_id,
vec![],
UserOperation::CreateCommentLike,
context,
)
.await
}
async fn undo_like_or_dislike_comment(
actor: &Url,
object: &Url,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?;
let comment = get_or_fetch_and_insert_comment(object, context, request_counter).await?;
let comment_id = comment.id;
let person_id = actor.id;
blocking(context.pool(), move |conn| {
CommentLike::remove(conn, person_id, comment_id)
})
.await??;
send_websocket_message(
comment.id,
vec![],
UserOperation::CreateCommentLike,
context,
)
.await
}

View file

@ -26,7 +26,7 @@ impl ActivityHandlerNew for RemoveComment {
async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
check_is_apub_id_valid(&self.common.actor, false)?; check_is_apub_id_valid(&self.common.actor, false)?;
verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await
} }
async fn receive( async fn receive(

View file

@ -1,51 +0,0 @@
use crate::activities::comment::{dislike::DislikeComment, undo_like_or_dislike_comment};
use activitystreams::activity::kind::UndoType;
use lemmy_apub::check_is_apub_id_valid;
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UndoDislikeComment {
to: PublicUrl,
object: DislikeComment,
cc: [Url; 1],
#[serde(rename = "type")]
kind: UndoType,
#[serde(flatten)]
common: ActivityCommonFields,
}
#[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for UndoDislikeComment {
async fn verify(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
verify_domains_match(&self.common.actor, &self.object.object)?;
check_is_apub_id_valid(&self.common.actor, false)?;
self.object.verify(context, request_counter).await
}
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
undo_like_or_dislike_comment(
&self.common.actor,
&self.object.object,
context,
request_counter,
)
.await
}
fn common(&self) -> &ActivityCommonFields {
&self.common
}
}

View file

@ -33,7 +33,7 @@ impl ActivityHandlerNew for UndoRemoveComment {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
check_is_apub_id_valid(&self.common.actor, false)?; check_is_apub_id_valid(&self.common.actor, false)?;
verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
self.object.verify(context, request_counter).await self.object.verify(context, request_counter).await
} }

View file

@ -1,7 +1,16 @@
use crate::activities::comment::{get_notif_recipients, send_websocket_message}; use crate::activities::{
comment::{get_notif_recipients, send_websocket_message},
verify_activity,
verify_person_in_community,
};
use activitystreams::{activity::kind::UpdateType, base::BaseExt}; use activitystreams::{activity::kind::UpdateType, base::BaseExt};
use lemmy_apub::{check_is_apub_id_valid, objects::FromApub, NoteExt}; use lemmy_apub::{objects::FromApub, NoteExt};
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; use lemmy_apub_lib::{
verify_domains_match_opt,
ActivityCommonFields,
ActivityHandlerNew,
PublicUrl,
};
use lemmy_db_schema::source::comment::Comment; use lemmy_db_schema::source::comment::Comment;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{LemmyContext, UserOperationCrud}; use lemmy_websocket::{LemmyContext, UserOperationCrud};
@ -21,10 +30,15 @@ pub struct UpdateComment {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for UpdateComment { impl ActivityHandlerNew for UpdateComment {
async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; &self,
self.object.id(self.common.actor.as_str())?; context: &LemmyContext,
check_is_apub_id_valid(&self.common.actor, false) request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_activity(self.common())?;
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
Ok(())
} }
async fn receive( async fn receive(

View file

@ -32,7 +32,7 @@ impl ActivityHandlerNew for AddMod {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
verify_domains_match(&self.target, &self.cc[0])?; verify_domains_match(&self.target, &self.cc[0])?;
check_is_apub_id_valid(&self.common.actor, false)?; check_is_apub_id_valid(&self.common.actor, false)?;
verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
verify_add_remove_moderator_target(&self.target, self.cc[0].clone()) verify_add_remove_moderator_target(&self.target, self.cc[0].clone())
} }

View file

@ -1,14 +1,17 @@
use activitystreams::activity::kind::AnnounceType;
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url;
use crate::{ use crate::{
activities::{ activities::{
comment::{ comment::{
create::CreateComment, create::CreateComment,
delete::DeleteComment, delete::DeleteComment,
dislike::DislikeComment,
like::LikeComment,
remove::RemoveComment, remove::RemoveComment,
undo_delete::UndoDeleteComment, undo_delete::UndoDeleteComment,
undo_dislike::UndoDislikeComment,
undo_like::UndoLikeComment,
undo_remove::UndoRemoveComment, undo_remove::UndoRemoveComment,
update::UpdateComment, update::UpdateComment,
}, },
@ -16,48 +19,42 @@ use crate::{
post::{ post::{
create::CreatePost, create::CreatePost,
delete::DeletePost, delete::DeletePost,
dislike::DislikePost,
like::LikePost,
remove::RemovePost, remove::RemovePost,
undo_delete::UndoDeletePost, undo_delete::UndoDeletePost,
undo_dislike::UndoDislikePost,
undo_like::UndoLikePost,
undo_remove::UndoRemovePost, undo_remove::UndoRemovePost,
update::UpdatePost, update::UpdatePost,
}, },
post_or_comment::{
dislike::DislikePostOrComment,
like::LikePostOrComment,
undo_dislike::UndoDislikePostOrComment,
undo_like::UndoLikePostOrComment,
},
verify_activity,
verify_community,
}, },
http::is_activity_already_known, http::is_activity_already_known,
}; };
use activitystreams::activity::kind::RemoveType;
use lemmy_apub::check_is_apub_id_valid;
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandlerNew)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandlerNew)]
#[serde(untagged)]
pub enum AnnouncableActivities { pub enum AnnouncableActivities {
CreateComment(CreateComment), CreateComment(CreateComment),
UpdateComment(UpdateComment), UpdateComment(UpdateComment),
LikeComment(LikeComment),
DislikeComment(DislikeComment),
UndoLikeComment(UndoLikeComment),
UndoDislikeComment(UndoDislikeComment),
DeleteComment(DeleteComment), DeleteComment(DeleteComment),
UndoDeleteComment(UndoDeleteComment), UndoDeleteComment(UndoDeleteComment),
RemoveComment(RemoveComment), RemoveComment(RemoveComment),
UndoRemoveComment(UndoRemoveComment), UndoRemoveComment(UndoRemoveComment),
CreatePost(CreatePost), CreatePost(CreatePost),
UpdatePost(UpdatePost), UpdatePost(UpdatePost),
LikePost(LikePost),
DislikePost(DislikePost),
DeletePost(DeletePost), DeletePost(DeletePost),
UndoDeletePost(UndoDeletePost), UndoDeletePost(UndoDeletePost),
RemovePost(RemovePost), RemovePost(RemovePost),
UndoRemovePost(UndoRemovePost), UndoRemovePost(UndoRemovePost),
UndoLikePost(UndoLikePost), LikePostOrComment(LikePostOrComment),
UndoDislikePost(UndoDislikePost), DislikePostOrComment(DislikePostOrComment),
UndoLikePostOrComment(UndoLikePostOrComment),
UndoDislikePostOrComment(UndoDislikePostOrComment),
BlockUserFromCommunity(BlockUserFromCommunity), BlockUserFromCommunity(BlockUserFromCommunity),
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
} }
@ -69,7 +66,7 @@ pub struct AnnounceActivity {
object: AnnouncableActivities, object: AnnouncableActivities,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: RemoveType, kind: AnnounceType,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, common: ActivityCommonFields,
} }
@ -81,10 +78,9 @@ impl ActivityHandlerNew for AnnounceActivity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_activity(self.common())?;
verify_domains_match(&self.common.actor, &self.cc[0])?; verify_community(&self.common.actor, context, request_counter).await?;
check_is_apub_id_valid(&self.common.actor, false)?; Ok(())
self.object.verify(context, request_counter).await
} }
async fn receive( async fn receive(

View file

@ -34,7 +34,7 @@ impl ActivityHandlerNew for BlockUserFromCommunity {
async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
check_is_apub_id_valid(&self.common.actor, false)?; check_is_apub_id_valid(&self.common.actor, false)?;
verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await
} }
async fn receive( async fn receive(

View file

@ -32,7 +32,7 @@ impl ActivityHandlerNew for RemoveMod {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
verify_domains_match(&self.target, &self.cc[0])?; verify_domains_match(&self.target, &self.cc[0])?;
check_is_apub_id_valid(&self.common.actor, false)?; check_is_apub_id_valid(&self.common.actor, false)?;
verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
verify_add_remove_moderator_target(&self.target, self.cc[0].clone()) verify_add_remove_moderator_target(&self.target, self.cc[0].clone())
} }

View file

@ -33,7 +33,7 @@ impl ActivityHandlerNew for UndoBlockUserFromCommunity {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
check_is_apub_id_valid(&self.common.actor, false)?; check_is_apub_id_valid(&self.common.actor, false)?;
verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
self.object.verify(context, request_counter).await self.object.verify(context, request_counter).await
} }

View file

@ -1,12 +1,16 @@
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub::{ use lemmy_apub::{
check_community_or_site_ban,
check_is_apub_id_valid, check_is_apub_id_valid,
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
}; };
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields}; use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields};
use lemmy_db_queries::ApubObject; use lemmy_db_queries::ApubObject;
use lemmy_db_schema::source::{community::Community, person::Person}; use lemmy_db_schema::{
source::{community::Community, person::Person},
DbUrl,
};
use lemmy_db_views_actor::community_view::CommunityView; use lemmy_db_views_actor::community_view::CommunityView;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -16,6 +20,7 @@ pub mod comment;
pub mod community; pub mod community;
pub mod following; pub mod following;
pub mod post; pub mod post;
pub mod post_or_comment;
pub mod private_message; pub mod private_message;
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person /// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
@ -32,6 +37,29 @@ async fn verify_person(
Ok(()) Ok(())
} }
/// Fetches the person and community to verify their type, then checks if person is banned from site
/// or community.
async fn verify_person_in_community(
person_id: &Url,
cc: &[Url],
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<Community, LemmyError> {
let person = get_or_fetch_and_upsert_person(person_id, context, request_counter).await?;
let mut cc_iter = cc.iter();
let community: Community = loop {
if let Some(cid) = cc_iter.next() {
if let Ok(c) = get_or_fetch_and_upsert_community(cid, context, request_counter).await {
break c;
}
} else {
return Err(anyhow!("No community found in cc").into());
}
};
check_community_or_site_ban(&person, community.id, context.pool()).await?;
Ok(community)
}
/// Simply check that the url actually refers to a valid group. /// Simply check that the url actually refers to a valid group.
async fn verify_community( async fn verify_community(
community_id: &Url, community_id: &Url,
@ -49,7 +77,7 @@ fn verify_activity(common: &ActivityCommonFields) -> Result<(), LemmyError> {
} }
async fn verify_mod_action( async fn verify_mod_action(
actor_id: Url, actor_id: &Url,
activity_cc: Url, activity_cc: Url,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
@ -59,8 +87,9 @@ async fn verify_mod_action(
.await??; .await??;
if community.local { if community.local {
let actor_id: DbUrl = actor_id.clone().into();
let actor = blocking(context.pool(), move |conn| { let actor = blocking(context.pool(), move |conn| {
Person::read_from_apub_id(conn, &actor_id.into()) Person::read_from_apub_id(conn, &actor_id)
}) })
.await??; .await??;

View file

@ -1,13 +1,21 @@
use crate::activities::post::send_websocket_message; use crate::activities::{
post::send_websocket_message,
verify_activity,
verify_person_in_community,
};
use activitystreams::{activity::kind::CreateType, base::BaseExt}; use activitystreams::{activity::kind::CreateType, base::BaseExt};
use lemmy_apub::{ use lemmy_apub::{
check_is_apub_id_valid,
fetcher::person::get_or_fetch_and_upsert_person, fetcher::person::get_or_fetch_and_upsert_person,
objects::FromApub, objects::FromApub,
ActorType, ActorType,
PageExt, PageExt,
}; };
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; use lemmy_apub_lib::{
verify_domains_match_opt,
ActivityCommonFields,
ActivityHandlerNew,
PublicUrl,
};
use lemmy_db_schema::source::post::Post; use lemmy_db_schema::source::post::Post;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{LemmyContext, UserOperationCrud}; use lemmy_websocket::{LemmyContext, UserOperationCrud};
@ -27,10 +35,15 @@ pub struct CreatePost {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for CreatePost { impl ActivityHandlerNew for CreatePost {
async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(
verify_domains_match(self.common.id_unchecked(), &self.common.actor)?; &self,
self.object.id(self.common.actor.as_str())?; context: &LemmyContext,
check_is_apub_id_valid(&self.common.actor, false) request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_activity(self.common())?;
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
Ok(())
} }
async fn receive( async fn receive(

View file

@ -1,46 +0,0 @@
use crate::activities::post::like_or_dislike_post;
use activitystreams::activity::kind::DislikeType;
use lemmy_apub::check_is_apub_id_valid;
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DislikePost {
to: PublicUrl,
pub(in crate::activities::post) object: Url,
cc: [Url; 1],
#[serde(rename = "type")]
kind: DislikeType,
#[serde(flatten)]
common: ActivityCommonFields,
}
#[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for DislikePost {
async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
check_is_apub_id_valid(&self.common.actor, false)
}
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
like_or_dislike_post(
-1,
&self.common.actor,
&self.object,
context,
request_counter,
)
.await
}
fn common(&self) -> &ActivityCommonFields {
&self.common
}
}

View file

@ -1,30 +1,19 @@
use lemmy_api_common::{blocking, post::PostResponse}; use lemmy_api_common::{blocking, post::PostResponse};
use lemmy_apub::fetcher::{ use lemmy_db_schema::PostId;
objects::get_or_fetch_and_insert_post,
person::get_or_fetch_and_upsert_person,
};
use lemmy_db_queries::Likeable;
use lemmy_db_schema::{
source::post::{PostLike, PostLikeForm},
PostId,
};
use lemmy_db_views::post_view::PostView; use lemmy_db_views::post_view::PostView;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendPost, LemmyContext};
use url::Url;
pub mod create; pub mod create;
pub mod delete; pub mod delete;
pub mod dislike;
pub mod like;
pub mod remove; pub mod remove;
pub mod undo_delete; pub mod undo_delete;
pub mod undo_dislike;
pub mod undo_like;
pub mod undo_remove; pub mod undo_remove;
pub mod update; pub mod update;
async fn send_websocket_message<OP: ToString + Send + lemmy_websocket::OperationType + 'static>( pub(crate) async fn send_websocket_message<
OP: ToString + Send + lemmy_websocket::OperationType + 'static,
>(
post_id: PostId, post_id: PostId,
op: OP, op: OP,
context: &LemmyContext, context: &LemmyContext,
@ -44,47 +33,3 @@ async fn send_websocket_message<OP: ToString + Send + lemmy_websocket::Operation
Ok(()) Ok(())
} }
async fn like_or_dislike_post(
score: i16,
actor: &Url,
object: &Url,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?;
let post = get_or_fetch_and_insert_post(object, context, request_counter).await?;
let post_id = post.id;
let like_form = PostLikeForm {
post_id: post.id,
person_id: actor.id,
score,
};
let person_id = actor.id;
blocking(context.pool(), move |conn| {
PostLike::remove(conn, person_id, post_id)?;
PostLike::like(conn, &like_form)
})
.await??;
send_websocket_message(post.id, UserOperation::CreatePostLike, context).await
}
async fn undo_like_or_dislike_post(
actor: &Url,
object: &Url,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?;
let post = get_or_fetch_and_insert_post(object, context, request_counter).await?;
let post_id = post.id;
let person_id = actor.id;
blocking(context.pool(), move |conn| {
PostLike::remove(conn, person_id, post_id)
})
.await??;
send_websocket_message(post.id, UserOperation::CreatePostLike, context).await
}

View file

@ -26,7 +26,7 @@ impl ActivityHandlerNew for RemovePost {
async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
check_is_apub_id_valid(&self.common.actor, false)?; check_is_apub_id_valid(&self.common.actor, false)?;
verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await
} }
async fn receive( async fn receive(

View file

@ -1,50 +0,0 @@
use crate::activities::post::{like::LikePost, undo_like_or_dislike_post};
use activitystreams::activity::kind::UndoType;
use lemmy_apub::check_is_apub_id_valid;
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UndoLikePost {
to: PublicUrl,
object: LikePost,
cc: [Url; 1],
#[serde(rename = "type")]
kind: UndoType,
#[serde(flatten)]
common: ActivityCommonFields,
}
#[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for UndoLikePost {
async fn verify(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
verify_domains_match(&self.common.actor, &self.object.object)?;
check_is_apub_id_valid(&self.common.actor, false)?;
self.object.verify(context, request_counter).await
}
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
undo_like_or_dislike_post(
&self.common.actor,
&self.object.object,
context,
request_counter,
)
.await
}
fn common(&self) -> &ActivityCommonFields {
&self.common
}
}

View file

@ -33,7 +33,7 @@ impl ActivityHandlerNew for UndoRemovePost {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
check_is_apub_id_valid(&self.common.actor, false)?; check_is_apub_id_valid(&self.common.actor, false)?;
verify_mod_action(self.common.actor.clone(), self.cc[0].clone(), context).await?; verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
self.object.verify(context, request_counter).await self.object.verify(context, request_counter).await
} }

View file

@ -1,19 +1,26 @@
use crate::activities::post::send_websocket_message; use crate::activities::{
post::send_websocket_message,
verify_activity,
verify_mod_action,
verify_person_in_community,
};
use activitystreams::{activity::kind::UpdateType, base::BaseExt}; use activitystreams::{activity::kind::UpdateType, base::BaseExt};
use anyhow::Context; use anyhow::Context;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub::{ use lemmy_apub::{
check_is_apub_id_valid,
objects::{FromApub, FromApubToForm}, objects::{FromApub, FromApubToForm},
ActorType,
PageExt, PageExt,
}; };
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl}; use lemmy_apub_lib::{
use lemmy_db_queries::{ApubObject, Crud}; verify_domains_match_opt,
ActivityCommonFields,
ActivityHandlerNew,
PublicUrl,
};
use lemmy_db_queries::ApubObject;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::post::{Post, PostForm},
community::Community,
post::{Post, PostForm},
},
DbUrl, DbUrl,
}; };
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
@ -34,17 +41,16 @@ pub struct UpdatePost {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for UpdatePost { impl ActivityHandlerNew for UpdatePost {
async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(
verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
self.object.id(self.common.actor.as_str())?;
check_is_apub_id_valid(&self.common.actor, false)
}
async fn receive(
&self, &self,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?;
let community =
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
let temp_post = PostForm::from_apub( let temp_post = PostForm::from_apub(
&self.object, &self.object,
context, context,
@ -53,36 +59,32 @@ impl ActivityHandlerNew for UpdatePost {
false, false,
) )
.await?; .await?;
let post_id: DbUrl = temp_post.ap_id.context(location_info!())?; let post_id: DbUrl = temp_post.ap_id.context(location_info!())?;
let old_post = blocking(context.pool(), move |conn| { let old_post = blocking(context.pool(), move |conn| {
Post::read_from_apub_id(conn, &post_id) Post::read_from_apub_id(conn, &post_id)
}) })
.await??; .await??;
// If sticked or locked state was changed, make sure the actor is a mod
let stickied = temp_post.stickied.context(location_info!())?; let stickied = temp_post.stickied.context(location_info!())?;
let locked = temp_post.locked.context(location_info!())?; let locked = temp_post.locked.context(location_info!())?;
let mut mod_action_allowed = false;
if (stickied != old_post.stickied) || (locked != old_post.locked) { if (stickied != old_post.stickied) || (locked != old_post.locked) {
let community = blocking(context.pool(), move |conn| { verify_mod_action(&self.common.actor, community.actor_id(), context).await?;
Community::read(conn, old_post.community_id)
})
.await??;
// Only check mod status if the community is local, otherwise we trust that it was sent correctly.
if community.local {
// TODO
//verify_mod_activity(&update, announce, &community, context).await?;
}
mod_action_allowed = true;
} }
Ok(())
}
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let post = Post::from_apub( let post = Post::from_apub(
&self.object, &self.object,
context, context,
self.common.actor.clone(), self.common.actor.clone(),
request_counter, request_counter,
mod_action_allowed, // TODO: we already check here if the mod action is valid, can remove that check param
true,
) )
.await?; .await?;

View file

@ -1,16 +1,19 @@
use crate::activities::comment::like_or_dislike_comment; use crate::activities::{
post_or_comment::voting::receive_like_or_dislike,
verify_activity,
verify_person_in_community,
};
use activitystreams::activity::kind::DislikeType; use activitystreams::activity::kind::DislikeType;
use lemmy_apub::check_is_apub_id_valid; use lemmy_apub_lib::{ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct DislikeComment { pub struct DislikePostOrComment {
to: PublicUrl, to: PublicUrl,
pub(in crate::activities::comment) object: Url, pub(in crate::activities) object: Url,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: DislikeType, kind: DislikeType,
@ -19,10 +22,15 @@ pub struct DislikeComment {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for DislikeComment { impl ActivityHandlerNew for DislikePostOrComment {
async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; &self,
check_is_apub_id_valid(&self.common.actor, false) context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_activity(self.common())?;
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
Ok(())
} }
async fn receive( async fn receive(
@ -30,7 +38,7 @@ impl ActivityHandlerNew for DislikeComment {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
like_or_dislike_comment( receive_like_or_dislike(
-1, -1,
&self.common.actor, &self.common.actor,
&self.object, &self.object,

View file

@ -1,16 +1,19 @@
use crate::activities::post::like_or_dislike_post; use crate::activities::{
post_or_comment::voting::receive_like_or_dislike,
verify_activity,
verify_person_in_community,
};
use activitystreams::activity::kind::LikeType; use activitystreams::activity::kind::LikeType;
use lemmy_apub::check_is_apub_id_valid; use lemmy_apub_lib::{ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct LikePost { pub struct LikePostOrComment {
to: PublicUrl, to: PublicUrl,
pub(in crate::activities::post) object: Url, pub(in crate::activities::post_or_comment) object: Url,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: LikeType, kind: LikeType,
@ -19,10 +22,15 @@ pub struct LikePost {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for LikePost { impl ActivityHandlerNew for LikePostOrComment {
async fn verify(&self, _context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> { async fn verify(
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; &self,
check_is_apub_id_valid(&self.common.actor, false) context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_activity(self.common())?;
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
Ok(())
} }
async fn receive( async fn receive(
@ -30,7 +38,7 @@ impl ActivityHandlerNew for LikePost {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
like_or_dislike_post( receive_like_or_dislike(
1, 1,
&self.common.actor, &self.common.actor,
&self.object, &self.object,

View file

@ -0,0 +1,5 @@
pub mod dislike;
pub mod like;
pub mod undo_dislike;
pub mod undo_like;
mod voting;

View file

@ -1,16 +1,19 @@
use crate::activities::post::{dislike::DislikePost, undo_like_or_dislike_post}; use crate::activities::{
post_or_comment::{dislike::DislikePostOrComment, voting::receive_undo_like_or_dislike},
verify_activity,
verify_person_in_community,
};
use activitystreams::activity::kind::UndoType; use activitystreams::activity::kind::UndoType;
use lemmy_apub::check_is_apub_id_valid; use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoDislikePost { pub struct UndoDislikePostOrComment {
to: PublicUrl, to: PublicUrl,
object: DislikePost, object: DislikePostOrComment,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
@ -19,16 +22,17 @@ pub struct UndoDislikePost {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for UndoDislikePost { impl ActivityHandlerNew for UndoDislikePostOrComment {
async fn verify( async fn verify(
&self, &self,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_activity(self.common())?;
verify_domains_match(&self.common.actor, &self.object.object)?; verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
check_is_apub_id_valid(&self.common.actor, false)?; verify_urls_match(&self.common.actor, &self.object.common().actor)?;
self.object.verify(context, request_counter).await self.object.verify(context, request_counter).await?;
Ok(())
} }
async fn receive( async fn receive(
@ -36,7 +40,7 @@ impl ActivityHandlerNew for UndoDislikePost {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
undo_like_or_dislike_post( receive_undo_like_or_dislike(
&self.common.actor, &self.common.actor,
&self.object.object, &self.object.object,
context, context,

View file

@ -1,16 +1,19 @@
use crate::activities::comment::{like::LikeComment, undo_like_or_dislike_comment}; use crate::activities::{
post_or_comment::{like::LikePostOrComment, voting::receive_undo_like_or_dislike},
verify_activity,
verify_person_in_community,
};
use activitystreams::activity::kind::UndoType; use activitystreams::activity::kind::UndoType;
use lemmy_apub::check_is_apub_id_valid; use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoLikeComment { pub struct UndoLikePostOrComment {
to: PublicUrl, to: PublicUrl,
object: LikeComment, object: LikePostOrComment,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
@ -19,16 +22,17 @@ pub struct UndoLikeComment {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for UndoLikeComment { impl ActivityHandlerNew for UndoLikePostOrComment {
async fn verify( async fn verify(
&self, &self,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(&self.common.actor, self.common.id_unchecked())?; verify_activity(self.common())?;
verify_domains_match(&self.common.actor, &self.object.object)?; verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
check_is_apub_id_valid(&self.common.actor, false)?; verify_urls_match(&self.common.actor, &self.object.common().actor)?;
self.object.verify(context, request_counter).await self.object.verify(context, request_counter).await?;
Ok(())
} }
async fn receive( async fn receive(
@ -36,7 +40,7 @@ impl ActivityHandlerNew for UndoLikeComment {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
undo_like_or_dislike_comment( receive_undo_like_or_dislike(
&self.common.actor, &self.common.actor,
&self.object.object, &self.object.object,
context, context,

View file

@ -0,0 +1,152 @@
use crate::activities::{
comment::send_websocket_message as send_comment_websocket_message,
post::send_websocket_message as send_post_websocket_message,
};
use lemmy_api_common::blocking;
use lemmy_apub::{
fetcher::{
objects::get_or_fetch_and_insert_post_or_comment,
person::get_or_fetch_and_upsert_person,
},
PostOrComment,
};
use lemmy_db_queries::Likeable;
use lemmy_db_schema::source::{
comment::{Comment, CommentLike, CommentLikeForm},
post::{Post, PostLike, PostLikeForm},
};
use lemmy_utils::LemmyError;
use lemmy_websocket::{LemmyContext, UserOperation};
use std::ops::Deref;
use url::Url;
pub(in crate::activities::post_or_comment) async fn receive_like_or_dislike(
score: i16,
actor: &Url,
object: &Url,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? {
PostOrComment::Post(p) => {
like_or_dislike_post(score, actor, p.deref(), context, request_counter).await
}
PostOrComment::Comment(c) => {
like_or_dislike_comment(score, actor, c.deref(), context, request_counter).await
}
}
}
async fn like_or_dislike_comment(
score: i16,
actor: &Url,
comment: &Comment,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?;
let comment_id = comment.id;
let like_form = CommentLikeForm {
comment_id,
post_id: comment.post_id,
person_id: actor.id,
score,
};
let person_id = actor.id;
blocking(context.pool(), move |conn| {
CommentLike::remove(conn, person_id, comment_id)?;
CommentLike::like(conn, &like_form)
})
.await??;
send_comment_websocket_message(
comment_id,
vec![],
UserOperation::CreateCommentLike,
context,
)
.await
}
async fn like_or_dislike_post(
score: i16,
actor: &Url,
post: &Post,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?;
let post_id = post.id;
let like_form = PostLikeForm {
post_id: post.id,
person_id: actor.id,
score,
};
let person_id = actor.id;
blocking(context.pool(), move |conn| {
PostLike::remove(conn, person_id, post_id)?;
PostLike::like(conn, &like_form)
})
.await??;
send_post_websocket_message(post.id, UserOperation::CreatePostLike, context).await
}
pub(in crate::activities::post_or_comment) async fn receive_undo_like_or_dislike(
actor: &Url,
object: &Url,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? {
PostOrComment::Post(p) => {
undo_like_or_dislike_post(actor, p.deref(), context, request_counter).await
}
PostOrComment::Comment(c) => {
undo_like_or_dislike_comment(actor, c.deref(), context, request_counter).await
}
}
}
async fn undo_like_or_dislike_comment(
actor: &Url,
comment: &Comment,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?;
let comment_id = comment.id;
let person_id = actor.id;
blocking(context.pool(), move |conn| {
CommentLike::remove(conn, person_id, comment_id)
})
.await??;
send_comment_websocket_message(
comment.id,
vec![],
UserOperation::CreateCommentLike,
context,
)
.await
}
async fn undo_like_or_dislike_post(
actor: &Url,
post: &Post,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?;
let post_id = post.id;
let person_id = actor.id;
blocking(context.pool(), move |conn| {
PostLike::remove(conn, person_id, post_id)
})
.await??;
send_post_websocket_message(post.id, UserOperation::CreatePostLike, context).await
}

View file

@ -2,12 +2,8 @@ use crate::activities::{
comment::{ comment::{
create::CreateComment, create::CreateComment,
delete::DeleteComment, delete::DeleteComment,
dislike::DislikeComment,
like::LikeComment,
remove::RemoveComment, remove::RemoveComment,
undo_delete::UndoDeleteComment, undo_delete::UndoDeleteComment,
undo_dislike::UndoDislikeComment,
undo_like::UndoLikeComment,
undo_remove::UndoRemoveComment, undo_remove::UndoRemoveComment,
update::UpdateComment, update::UpdateComment,
}, },
@ -27,15 +23,17 @@ use crate::activities::{
post::{ post::{
create::CreatePost, create::CreatePost,
delete::DeletePost, delete::DeletePost,
dislike::DislikePost,
like::LikePost,
remove::RemovePost, remove::RemovePost,
undo_delete::UndoDeletePost, undo_delete::UndoDeletePost,
undo_dislike::UndoDislikePost,
undo_like::UndoLikePost,
undo_remove::UndoRemovePost, undo_remove::UndoRemovePost,
update::UpdatePost, update::UpdatePost,
}, },
post_or_comment::{
dislike::DislikePostOrComment,
like::LikePostOrComment,
undo_dislike::UndoDislikePostOrComment,
undo_like::UndoLikePostOrComment,
},
private_message::{ private_message::{
create::CreatePrivateMessage, create::CreatePrivateMessage,
delete::DeletePrivateMessage, delete::DeletePrivateMessage,
@ -66,24 +64,20 @@ pub enum GroupInboxActivities {
UndoFollowCommunity(UndoFollowCommunity), UndoFollowCommunity(UndoFollowCommunity),
CreateComment(CreateComment), CreateComment(CreateComment),
UpdateComment(UpdateComment), UpdateComment(UpdateComment),
LikeComment(LikeComment),
DislikeComment(DislikeComment),
UndoLikeComment(UndoLikeComment),
UndoDislikeComment(UndoDislikeComment),
DeleteComment(DeleteComment), DeleteComment(DeleteComment),
UndoDeleteComment(UndoDeleteComment), UndoDeleteComment(UndoDeleteComment),
RemoveComment(RemoveComment), RemoveComment(RemoveComment),
UndoRemoveComment(UndoRemoveComment), UndoRemoveComment(UndoRemoveComment),
CreatePost(CreatePost), CreatePost(CreatePost),
UpdatePost(UpdatePost), UpdatePost(UpdatePost),
LikePost(LikePost),
DislikePost(DislikePost),
DeletePost(DeletePost), DeletePost(DeletePost),
UndoDeletePost(UndoDeletePost), UndoDeletePost(UndoDeletePost),
RemovePost(RemovePost), RemovePost(RemovePost),
UndoRemovePost(UndoRemovePost), UndoRemovePost(UndoRemovePost),
UndoLikePost(UndoLikePost), LikePostOrComment(LikePostOrComment),
UndoDislikePost(UndoDislikePost), DislikePostOrComment(DislikePostOrComment),
UndoLikePostOrComment(UndoLikePostOrComment),
UndoDislikePostOrComment(UndoDislikePostOrComment),
UpdateCommunity(Box<UpdateCommunity>), UpdateCommunity(Box<UpdateCommunity>),
DeleteCommunity(DeleteCommunity), DeleteCommunity(DeleteCommunity),
RemoveCommunity(RemoveCommunity), RemoveCommunity(RemoveCommunity),
@ -98,36 +92,25 @@ pub enum GroupInboxActivities {
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandlerNew)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandlerNew)]
#[serde(untagged)] #[serde(untagged)]
pub enum SharedInboxActivities { pub enum SharedInboxActivities {
// received by person
AcceptFollowCommunity(AcceptFollowCommunity),
CreatePrivateMessage(CreatePrivateMessage),
UpdatePrivateMessage(UpdatePrivateMessage),
DeletePrivateMessage(DeletePrivateMessage),
UndoDeletePrivateMessage(UndoDeletePrivateMessage),
AnnounceActivity(Box<AnnounceActivity>),
// received by group // received by group
FollowCommunity(FollowCommunity), FollowCommunity(FollowCommunity),
UndoFollowCommunity(UndoFollowCommunity), UndoFollowCommunity(UndoFollowCommunity),
CreateComment(CreateComment), CreateComment(CreateComment),
UpdateComment(UpdateComment), UpdateComment(UpdateComment),
LikeComment(LikeComment),
DislikeComment(DislikeComment),
UndoLikeComment(UndoLikeComment),
UndoDislikeComment(UndoDislikeComment),
DeleteComment(DeleteComment), DeleteComment(DeleteComment),
UndoDeleteComment(UndoDeleteComment), UndoDeleteComment(UndoDeleteComment),
RemoveComment(RemoveComment), RemoveComment(RemoveComment),
UndoRemoveComment(UndoRemoveComment), UndoRemoveComment(UndoRemoveComment),
CreatePost(CreatePost), CreatePost(CreatePost),
UpdatePost(UpdatePost), UpdatePost(UpdatePost),
LikePost(LikePost),
DislikePost(DislikePost),
DeletePost(DeletePost), DeletePost(DeletePost),
UndoDeletePost(UndoDeletePost), UndoDeletePost(UndoDeletePost),
RemovePost(RemovePost), RemovePost(RemovePost),
UndoRemovePost(UndoRemovePost), UndoRemovePost(UndoRemovePost),
UndoLikePost(UndoLikePost), LikePostOrComment(LikePostOrComment),
UndoDislikePost(UndoDislikePost), DislikePostOrComment(DislikePostOrComment),
UndoDislikePostOrComment(UndoDislikePostOrComment),
UndoLikePostOrComment(UndoLikePostOrComment),
UpdateCommunity(Box<UpdateCommunity>), UpdateCommunity(Box<UpdateCommunity>),
DeleteCommunity(DeleteCommunity), DeleteCommunity(DeleteCommunity),
RemoveCommunity(RemoveCommunity), RemoveCommunity(RemoveCommunity),
@ -137,4 +120,13 @@ pub enum SharedInboxActivities {
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
AddMod(AddMod), AddMod(AddMod),
RemoveMod(RemoveMod), RemoveMod(RemoveMod),
// received by person
AcceptFollowCommunity(AcceptFollowCommunity),
// Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
// avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
CreatePrivateMessage(CreatePrivateMessage),
UpdatePrivateMessage(UpdatePrivateMessage),
DeletePrivateMessage(DeletePrivateMessage),
UndoDeletePrivateMessage(UndoDeletePrivateMessage),
AnnounceActivity(Box<AnnounceActivity>),
} }

View file

@ -1,7 +1,4 @@
use crate::activities::{ use crate::http::inbox_enums::SharedInboxActivities;
following::accept::AcceptFollowCommunity,
post::{create::CreatePost, like::LikePost},
};
use actix_web::{ use actix_web::{
body::Body, body::Body,
web, web,
@ -20,12 +17,11 @@ use lemmy_apub::{
insert_activity, insert_activity,
APUB_JSON_CONTENT_TYPE, APUB_JSON_CONTENT_TYPE,
}; };
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandlerNew}; use lemmy_apub_lib::ActivityHandlerNew;
use lemmy_db_queries::{source::activity::Activity_, DbPool}; use lemmy_db_queries::{source::activity::Activity_, DbPool};
use lemmy_db_schema::source::activity::Activity; use lemmy_db_schema::source::activity::Activity;
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::debug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt::Debug, io::Read}; use std::{fmt::Debug, io::Read};
use url::Url; use url::Url;
@ -36,21 +32,13 @@ pub mod inbox_enums;
pub mod person; pub mod person;
pub mod post; pub mod post;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ActivityHandlerNew)]
#[serde(untagged)]
enum Ac {
CreatePost(CreatePost),
LikePost(LikePost),
AcceptFollowCommunity(AcceptFollowCommunity),
}
pub async fn shared_inbox( pub async fn shared_inbox(
request: HttpRequest, request: HttpRequest,
payload: Payload, payload: Payload,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> { ) -> Result<HttpResponse, LemmyError> {
let unparsed = payload_to_string(payload).await?; let unparsed = payload_to_string(payload).await?;
receive_activity::<Ac>(request, &unparsed, context).await receive_activity::<SharedInboxActivities>(request, &unparsed, context).await
} }
async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> { async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
@ -70,22 +58,23 @@ async fn receive_activity<'a, T>(
where where
T: ActivityHandlerNew + Clone + Deserialize<'a> + Serialize + std::fmt::Debug + Send + 'static, T: ActivityHandlerNew + Clone + Deserialize<'a> + Serialize + std::fmt::Debug + Send + 'static,
{ {
debug!("Received activity {}", activity); let activity = serde_json::from_str::<T>(activity);
let activity = serde_json::from_str::<T>(activity)?; dbg!(&activity);
let activity = activity?;
let activity_data = activity.common(); let activity_data = activity.common();
// TODO: which order to check things? // TODO: which order to check things?
// Do nothing if we received the same activity before // Do nothing if we received the same activity before
if is_activity_already_known(context.pool(), activity_data.id_unchecked()).await? { if is_activity_already_known(context.pool(), activity_data.id_unchecked()).await? {
return Ok(HttpResponse::Ok().finish()); return Ok(HttpResponse::Ok().finish());
} }
assert_activity_not_local(&activity)?;
check_is_apub_id_valid(&activity_data.actor, false)?;
let request_counter = &mut 0; let request_counter = &mut 0;
let actor = let actor =
get_or_fetch_and_upsert_actor(&activity_data.actor, &context, request_counter).await?; get_or_fetch_and_upsert_actor(&activity_data.actor, &context, request_counter).await?;
verify_signature(&request, &actor.public_key().context(location_info!())?)?; verify_signature(&request, &actor.public_key().context(location_info!())?)?;
activity.verify(&context, request_counter).await?; activity.verify(&context, request_counter).await?;
assert_activity_not_local(&activity)?;
check_is_apub_id_valid(&activity_data.actor, false)?;
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen // Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
// if we receive the same activity twice in very quick succession. // if we receive the same activity twice in very quick succession.

View file

@ -8,8 +8,8 @@ for ((i=0; i < times; i++)) ; do
echo "cargo clean" echo "cargo clean"
# to benchmark incremental compilation time, do a full build with the same compiler version first, # to benchmark incremental compilation time, do a full build with the same compiler version first,
# and use the following clean command: # and use the following clean command:
#cargo clean -p lemmy_utils cargo clean -p lemmy_utils
cargo clean #cargo clean
echo "cargo build" echo "cargo build"
start=$(date +%s.%N) start=$(date +%s.%N)
RUSTC_WRAPPER='' cargo build -q RUSTC_WRAPPER='' cargo build -q