mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-26 22:31:20 +00:00
commit
5d321949e6
92 changed files with 1043 additions and 1017 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1869,6 +1869,7 @@ dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"background-jobs",
|
"background-jobs",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
|
"diesel",
|
||||||
"http",
|
"http",
|
||||||
"http-signature-normalization-actix",
|
"http-signature-normalization-actix",
|
||||||
"http-signature-normalization-reqwest",
|
"http-signature-normalization-reqwest",
|
||||||
|
@ -1904,6 +1905,7 @@ dependencies = [
|
||||||
"diesel-derive-newtype",
|
"diesel-derive-newtype",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"lemmy_apub_lib",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"log",
|
"log",
|
||||||
"regex",
|
"regex",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
use crate::Perform;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -7,7 +7,8 @@ use lemmy_api_common::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
is_mod_or_admin,
|
is_mod_or_admin,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{fetcher::object_id::ObjectId, protocol::activities::community::report::Report};
|
use lemmy_apub::protocol::activities::community::report::Report;
|
||||||
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use lemmy_db_schema::{source::comment_report::*, traits::Reportable};
|
use lemmy_db_schema::{source::comment_report::*, traits::Reportable};
|
||||||
use lemmy_db_views::{
|
use lemmy_db_views::{
|
||||||
comment_report_view::{CommentReportQueryBuilder, CommentReportView},
|
comment_report_view::{CommentReportQueryBuilder, CommentReportView},
|
||||||
|
@ -16,8 +17,6 @@ use lemmy_db_views::{
|
||||||
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
||||||
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
||||||
|
|
||||||
use crate::Perform;
|
|
||||||
|
|
||||||
/// Creates a comment report and notifies the moderators of the community
|
/// Creates a comment report and notifies the moderators of the community
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for CreateCommentReport {
|
impl Perform for CreateCommentReport {
|
||||||
|
|
|
@ -73,7 +73,7 @@ impl Perform for CreatePostLike {
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let community_id = post.community_id;
|
let community_id = post.community_id;
|
||||||
let object = PostOrComment::Post(Box::new(post));
|
let object = PostOrComment::Post(post);
|
||||||
|
|
||||||
// Only add the like if the score isnt 0
|
// Only add the like if the score isnt 0
|
||||||
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
|
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
|
||||||
|
@ -169,7 +169,7 @@ impl Perform for LockPost {
|
||||||
|
|
||||||
// apub updates
|
// apub updates
|
||||||
CreateOrUpdatePost::send(
|
CreateOrUpdatePost::send(
|
||||||
&updated_post,
|
updated_post,
|
||||||
&local_user_view.person.clone().into(),
|
&local_user_view.person.clone().into(),
|
||||||
CreateOrUpdateType::Update,
|
CreateOrUpdateType::Update,
|
||||||
context,
|
context,
|
||||||
|
@ -242,7 +242,7 @@ impl Perform for StickyPost {
|
||||||
// Apub updates
|
// Apub updates
|
||||||
// TODO stickied should pry work like locked for ease of use
|
// TODO stickied should pry work like locked for ease of use
|
||||||
CreateOrUpdatePost::send(
|
CreateOrUpdatePost::send(
|
||||||
&updated_post,
|
updated_post,
|
||||||
&local_user_view.person.clone().into(),
|
&local_user_view.person.clone().into(),
|
||||||
CreateOrUpdateType::Update,
|
CreateOrUpdateType::Update,
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
use crate::Perform;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -13,7 +13,8 @@ use lemmy_api_common::{
|
||||||
ResolvePostReport,
|
ResolvePostReport,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_apub::{fetcher::object_id::ObjectId, protocol::activities::community::report::Report};
|
use lemmy_apub::protocol::activities::community::report::Report;
|
||||||
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::post_report::{PostReport, PostReportForm},
|
source::post_report::{PostReport, PostReportForm},
|
||||||
traits::Reportable,
|
traits::Reportable,
|
||||||
|
@ -25,8 +26,6 @@ use lemmy_db_views::{
|
||||||
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
||||||
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
||||||
|
|
||||||
use crate::Perform;
|
|
||||||
|
|
||||||
/// Creates a post report and notifies the moderators of the community
|
/// Creates a post report and notifies the moderators of the community
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for CreatePostReport {
|
impl Perform for CreatePostReport {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -13,6 +13,7 @@ use lemmy_api_common::{
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
fetcher::post_or_comment::PostOrComment,
|
fetcher::post_or_comment::PostOrComment,
|
||||||
generate_local_apub_endpoint,
|
generate_local_apub_endpoint,
|
||||||
|
objects::comment::ApubComment,
|
||||||
protocol::activities::{
|
protocol::activities::{
|
||||||
create_or_update::comment::CreateOrUpdateComment,
|
create_or_update::comment::CreateOrUpdateComment,
|
||||||
voting::vote::{Vote, VoteType},
|
voting::vote::{Vote, VoteType},
|
||||||
|
@ -40,8 +41,6 @@ use lemmy_websocket::{
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::PerformCrud;
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl PerformCrud for CreateComment {
|
impl PerformCrud for CreateComment {
|
||||||
type Response = CommentResponse;
|
type Response = CommentResponse;
|
||||||
|
@ -121,14 +120,6 @@ impl PerformCrud for CreateComment {
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| ApiError::err("couldnt_create_comment", e))?;
|
.map_err(|e| ApiError::err("couldnt_create_comment", e))?;
|
||||||
|
|
||||||
CreateOrUpdateComment::send(
|
|
||||||
&updated_comment.clone().into(),
|
|
||||||
&local_user_view.person.clone().into(),
|
|
||||||
CreateOrUpdateType::Create,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Scan the comment for user mentions, add those rows
|
// Scan the comment for user mentions, add those rows
|
||||||
let post_id = post.id;
|
let post_id = post.id;
|
||||||
let mentions = scrape_text_for_mentions(&comment_form.content);
|
let mentions = scrape_text_for_mentions(&comment_form.content);
|
||||||
|
@ -155,7 +146,15 @@ impl PerformCrud for CreateComment {
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| ApiError::err("couldnt_like_comment", e))?;
|
.map_err(|e| ApiError::err("couldnt_like_comment", e))?;
|
||||||
|
|
||||||
let object = PostOrComment::Comment(updated_comment.into());
|
let apub_comment: ApubComment = updated_comment.into();
|
||||||
|
CreateOrUpdateComment::send(
|
||||||
|
apub_comment.clone(),
|
||||||
|
&local_user_view.person.clone().into(),
|
||||||
|
CreateOrUpdateType::Create,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let object = PostOrComment::Comment(apub_comment);
|
||||||
Vote::send(
|
Vote::send(
|
||||||
&object,
|
&object,
|
||||||
&local_user_view.person.clone().into(),
|
&local_user_view.person.clone().into(),
|
||||||
|
|
|
@ -72,15 +72,6 @@ impl PerformCrud for EditComment {
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| ApiError::err("couldnt_update_comment", e))?;
|
.map_err(|e| ApiError::err("couldnt_update_comment", e))?;
|
||||||
|
|
||||||
// Send the apub update
|
|
||||||
CreateOrUpdateComment::send(
|
|
||||||
&updated_comment.clone().into(),
|
|
||||||
&local_user_view.person.clone().into(),
|
|
||||||
CreateOrUpdateType::Update,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Do the mentions / recipients
|
// Do the mentions / recipients
|
||||||
let updated_comment_content = updated_comment.content.to_owned();
|
let updated_comment_content = updated_comment.content.to_owned();
|
||||||
let mentions = scrape_text_for_mentions(&updated_comment_content);
|
let mentions = scrape_text_for_mentions(&updated_comment_content);
|
||||||
|
@ -94,6 +85,15 @@ impl PerformCrud for EditComment {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Send the apub update
|
||||||
|
CreateOrUpdateComment::send(
|
||||||
|
updated_comment.into(),
|
||||||
|
&local_user_view.person.into(),
|
||||||
|
CreateOrUpdateType::Update,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
send_comment_ws_message(
|
send_comment_ws_message(
|
||||||
data.comment_id,
|
data.comment_id,
|
||||||
UserOperationCrud::EditComment,
|
UserOperationCrud::EditComment,
|
||||||
|
|
|
@ -7,7 +7,6 @@ use lemmy_api_common::{
|
||||||
is_admin,
|
is_admin,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
generate_followers_url,
|
generate_followers_url,
|
||||||
generate_inbox_url,
|
generate_inbox_url,
|
||||||
generate_local_apub_endpoint,
|
generate_local_apub_endpoint,
|
||||||
|
@ -15,6 +14,7 @@ use lemmy_apub::{
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
EndpointType,
|
EndpointType,
|
||||||
};
|
};
|
||||||
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
diesel_option_overwrite_to_url,
|
diesel_option_overwrite_to_url,
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
use crate::PerformCrud;
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt};
|
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{get_actor_id_from_name, objects::community::ApubCommunity};
|
||||||
fetcher::object_id::ObjectId,
|
use lemmy_apub_lib::{object_id::ObjectId, webfinger::WebfingerType};
|
||||||
get_actor_id_from_name,
|
|
||||||
objects::community::ApubCommunity,
|
|
||||||
};
|
|
||||||
use lemmy_apub_lib::webfinger::WebfingerType;
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
from_opt_str_to_opt_enum,
|
from_opt_str_to_opt_enum,
|
||||||
traits::DeleteableOrRemoveable,
|
traits::DeleteableOrRemoveable,
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl PerformCrud for EditCommunity {
|
||||||
.map_err(|e| ApiError::err("couldnt_update_community", e))?;
|
.map_err(|e| ApiError::err("couldnt_update_community", e))?;
|
||||||
|
|
||||||
UpdateCommunity::send(
|
UpdateCommunity::send(
|
||||||
&updated_community.into(),
|
updated_community.into(),
|
||||||
&local_user_view.person.into(),
|
&local_user_view.person.into(),
|
||||||
context,
|
context,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use log::warn;
|
|
||||||
use webmention::{Webmention, WebmentionError};
|
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -14,6 +12,7 @@ use lemmy_api_common::{
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
fetcher::post_or_comment::PostOrComment,
|
fetcher::post_or_comment::PostOrComment,
|
||||||
generate_local_apub_endpoint,
|
generate_local_apub_endpoint,
|
||||||
|
objects::post::ApubPost,
|
||||||
protocol::activities::{
|
protocol::activities::{
|
||||||
create_or_update::post::CreateOrUpdatePost,
|
create_or_update::post::CreateOrUpdatePost,
|
||||||
voting::vote::{Vote, VoteType},
|
voting::vote::{Vote, VoteType},
|
||||||
|
@ -33,8 +32,9 @@ use lemmy_utils::{
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
||||||
|
use log::warn;
|
||||||
use crate::PerformCrud;
|
use url::Url;
|
||||||
|
use webmention::{Webmention, WebmentionError};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl PerformCrud for CreatePost {
|
impl PerformCrud for CreatePost {
|
||||||
|
@ -110,14 +110,6 @@ impl PerformCrud for CreatePost {
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| ApiError::err("couldnt_create_post", e))?;
|
.map_err(|e| ApiError::err("couldnt_create_post", e))?;
|
||||||
|
|
||||||
CreateOrUpdatePost::send(
|
|
||||||
&updated_post.clone().into(),
|
|
||||||
&local_user_view.person.clone().into(),
|
|
||||||
CreateOrUpdateType::Create,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// They like their own post by default
|
// They like their own post by default
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
let post_id = inserted_post.id;
|
let post_id = inserted_post.id;
|
||||||
|
@ -136,10 +128,8 @@ impl PerformCrud for CreatePost {
|
||||||
mark_post_as_read(person_id, post_id, context.pool()).await?;
|
mark_post_as_read(person_id, post_id, context.pool()).await?;
|
||||||
|
|
||||||
if let Some(url) = &updated_post.url {
|
if let Some(url) = &updated_post.url {
|
||||||
let mut webmention = Webmention::new(
|
let mut webmention =
|
||||||
updated_post.ap_id.clone().into_inner(),
|
Webmention::new::<Url>(updated_post.ap_id.clone().into(), url.clone().into())?;
|
||||||
url.clone().into_inner(),
|
|
||||||
)?;
|
|
||||||
webmention.set_checked(true);
|
webmention.set_checked(true);
|
||||||
match webmention.send().await {
|
match webmention.send().await {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
@ -148,7 +138,15 @@ impl PerformCrud for CreatePost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let object = PostOrComment::Post(Box::new(updated_post.into()));
|
let apub_post: ApubPost = updated_post.into();
|
||||||
|
CreateOrUpdatePost::send(
|
||||||
|
apub_post.clone(),
|
||||||
|
&local_user_view.person.clone().into(),
|
||||||
|
CreateOrUpdateType::Create,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let object = PostOrComment::Post(apub_post);
|
||||||
Vote::send(
|
Vote::send(
|
||||||
&object,
|
&object,
|
||||||
&local_user_view.person.clone().into(),
|
&local_user_view.person.clone().into(),
|
||||||
|
|
|
@ -109,7 +109,7 @@ impl PerformCrud for EditPost {
|
||||||
|
|
||||||
// Send apub update
|
// Send apub update
|
||||||
CreateOrUpdatePost::send(
|
CreateOrUpdatePost::send(
|
||||||
&updated_post.into(),
|
updated_post.into(),
|
||||||
&local_user_view.person.clone().into(),
|
&local_user_view.person.clone().into(),
|
||||||
CreateOrUpdateType::Update,
|
CreateOrUpdateType::Update,
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -83,7 +83,7 @@ impl PerformCrud for CreatePrivateMessage {
|
||||||
.map_err(|e| ApiError::err("couldnt_create_private_message", e))?;
|
.map_err(|e| ApiError::err("couldnt_create_private_message", e))?;
|
||||||
|
|
||||||
CreateOrUpdatePrivateMessage::send(
|
CreateOrUpdatePrivateMessage::send(
|
||||||
&updated_private_message.into(),
|
updated_private_message.into(),
|
||||||
&local_user_view.person.into(),
|
&local_user_view.person.into(),
|
||||||
CreateOrUpdateType::Create,
|
CreateOrUpdateType::Create,
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl PerformCrud for EditPrivateMessage {
|
||||||
|
|
||||||
// Send the apub update
|
// Send the apub update
|
||||||
CreateOrUpdatePrivateMessage::send(
|
CreateOrUpdatePrivateMessage::send(
|
||||||
&updated_private_message.into(),
|
updated_private_message.into(),
|
||||||
&local_user_view.person.into(),
|
&local_user_view.person.into(),
|
||||||
CreateOrUpdateType::Update,
|
CreateOrUpdateType::Update,
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
use crate::PerformCrud;
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
|
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{get_actor_id_from_name, objects::person::ApubPerson};
|
||||||
fetcher::object_id::ObjectId,
|
use lemmy_apub_lib::{object_id::ObjectId, webfinger::WebfingerType};
|
||||||
get_actor_id_from_name,
|
|
||||||
objects::person::ApubPerson,
|
|
||||||
};
|
|
||||||
use lemmy_apub_lib::webfinger::WebfingerType;
|
|
||||||
use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
|
use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
|
||||||
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
|
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::{
|
||||||
|
|
19
crates/apub/assets/lemmy/context.json
Normal file
19
crates/apub/assets/lemmy/context.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
{
|
||||||
|
"stickied": "as:stickied",
|
||||||
|
"pt": "https://join-lemmy.org#",
|
||||||
|
"sc": "http://schema.org#",
|
||||||
|
"matrixUserId": {
|
||||||
|
"type": "sc:Text",
|
||||||
|
"id": "as:alsoKnownAs"
|
||||||
|
},
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"comments_enabled": {
|
||||||
|
"type": "sc:Boolean",
|
||||||
|
"id": "pt:commentsEnabled"
|
||||||
|
},
|
||||||
|
"moderators": "as:moderators"
|
||||||
|
},
|
||||||
|
"https://w3id.org/security/v1"
|
||||||
|
]
|
|
@ -1,18 +1,3 @@
|
||||||
use activitystreams::public;
|
|
||||||
|
|
||||||
use lemmy_api_common::{blocking, check_post_deleted_or_removed};
|
|
||||||
use lemmy_apub_lib::{
|
|
||||||
data::Data,
|
|
||||||
traits::{ActivityHandler, ActorType, ApubObject},
|
|
||||||
verify::verify_domains_match,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
|
||||||
source::{community::Community, post::Post},
|
|
||||||
traits::Crud,
|
|
||||||
};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
check_community_deleted_or_removed,
|
check_community_deleted_or_removed,
|
||||||
|
@ -24,14 +9,27 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::{create_or_update::comment::CreateOrUpdateComment, CreateOrUpdateType},
|
protocol::activities::{create_or_update::comment::CreateOrUpdateComment, CreateOrUpdateType},
|
||||||
};
|
};
|
||||||
|
use activitystreams::public;
|
||||||
|
use lemmy_api_common::{blocking, check_post_deleted_or_removed};
|
||||||
|
use lemmy_apub_lib::{
|
||||||
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType, ApubObject},
|
||||||
|
verify::verify_domains_match,
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
source::{community::Community, post::Post},
|
||||||
|
traits::Crud,
|
||||||
|
};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
|
||||||
|
|
||||||
impl CreateOrUpdateComment {
|
impl CreateOrUpdateComment {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
comment: &ApubComment,
|
comment: ApubComment,
|
||||||
actor: &ApubPerson,
|
actor: &ApubPerson,
|
||||||
kind: CreateOrUpdateType,
|
kind: CreateOrUpdateType,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -50,12 +48,12 @@ impl CreateOrUpdateComment {
|
||||||
kind.clone(),
|
kind.clone(),
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?;
|
)?;
|
||||||
let maa = collect_non_local_mentions(comment, &community, context).await?;
|
let maa = collect_non_local_mentions(&comment, &community, context).await?;
|
||||||
|
|
||||||
let create_or_update = CreateOrUpdateComment {
|
let create_or_update = CreateOrUpdateComment {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: vec![public()],
|
to: vec![public()],
|
||||||
object: comment.to_apub(context).await?,
|
object: comment.into_apub(context).await?,
|
||||||
cc: maa.ccs,
|
cc: maa.ccs,
|
||||||
tag: maa.tags,
|
tag: maa.tags,
|
||||||
kind,
|
kind,
|
||||||
|
@ -77,19 +75,17 @@ impl ActivityHandler for CreateOrUpdateComment {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
let post = self.object.get_parents(context, request_counter).await?.0;
|
let post = self.object.get_parents(context, request_counter).await?.0;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
|
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||||
check_community_deleted_or_removed(&community)?;
|
check_community_deleted_or_removed(&community)?;
|
||||||
check_post_deleted_or_removed(&post)?;
|
check_post_deleted_or_removed(&post)?;
|
||||||
|
|
||||||
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
ApubComment::verify(&self.object, self.actor.inner(), context, request_counter).await?;
|
||||||
// comment deserialization)
|
|
||||||
self.object.verify(context, request_counter).await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,8 +94,7 @@ impl ActivityHandler for CreateOrUpdateComment {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let comment =
|
let comment = ApubComment::from_apub(self.object, context, request_counter).await?;
|
||||||
ApubComment::from_apub(&self.object, context, self.actor.inner(), request_counter).await?;
|
|
||||||
let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?;
|
let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?;
|
||||||
let notif_type = match self.kind {
|
let notif_type = match self.kind {
|
||||||
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
|
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use crate::{
|
use crate::objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson};
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
base::BaseExt,
|
base::BaseExt,
|
||||||
link::{LinkExt, Mention},
|
link::{LinkExt, Mention},
|
||||||
|
@ -9,7 +6,7 @@ use activitystreams::{
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{traits::ActorType, webfinger::WebfingerResponse};
|
use lemmy_apub_lib::{object_id::ObjectId, traits::ActorType, webfinger::WebfingerResponse};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::LocalUserId,
|
newtypes::LocalUserId,
|
||||||
source::{comment::Comment, person::Person, post::Post},
|
source::{comment::Comment, person::Person, post::Post},
|
||||||
|
|
|
@ -9,7 +9,6 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::community::add_mod::AddMod,
|
protocol::activities::community::add_mod::AddMod,
|
||||||
|
@ -18,6 +17,7 @@ use activitystreams::{activity::kind::AddType, public};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -64,8 +64,8 @@ impl ActivityHandler for AddMod {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_is_public},
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_is_public},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
http::{is_activity_already_known, ActivityCommonFields},
|
||||||
http::is_activity_already_known,
|
|
||||||
insert_activity,
|
insert_activity,
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
protocol::activities::community::announce::AnnounceActivity,
|
protocol::activities::community::announce::AnnounceActivity,
|
||||||
|
@ -10,7 +9,8 @@ use crate::{
|
||||||
use activitystreams::{activity::kind::AnnounceType, public};
|
use activitystreams::{activity::kind::AnnounceType, public};
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType},
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
@ -36,7 +36,7 @@ impl AnnounceActivity {
|
||||||
actor: ObjectId::new(community.actor_id()),
|
actor: ObjectId::new(community.actor_id()),
|
||||||
to: vec![public()],
|
to: vec![public()],
|
||||||
object,
|
object,
|
||||||
cc: vec![community.followers_url.clone().into_inner()],
|
cc: vec![community.followers_url.clone().into()],
|
||||||
kind: AnnounceType::Announce,
|
kind: AnnounceType::Announce,
|
||||||
id: generate_activity_id(
|
id: generate_activity_id(
|
||||||
&AnnounceType::Announce,
|
&AnnounceType::Announce,
|
||||||
|
@ -59,8 +59,8 @@ impl ActivityHandler for AnnounceActivity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -70,11 +70,15 @@ impl ActivityHandler for AnnounceActivity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
if is_activity_already_known(context.pool(), self.object.id_unchecked()).await? {
|
// TODO: this is pretty ugly, but i cant think of a much better way
|
||||||
|
let object = serde_json::to_string(&self.object)?;
|
||||||
|
let object_data: ActivityCommonFields = serde_json::from_str(&object)?;
|
||||||
|
|
||||||
|
if is_activity_already_known(context.pool(), &object_data.id).await? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
insert_activity(
|
insert_activity(
|
||||||
self.object.id_unchecked(),
|
&object_data.id,
|
||||||
self.object.clone(),
|
self.object.clone(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -8,7 +8,6 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::community::block_user::BlockUserFromCommunity,
|
protocol::activities::community::block_user::BlockUserFromCommunity,
|
||||||
};
|
};
|
||||||
|
@ -16,6 +15,7 @@ use activitystreams::{activity::kind::BlockType, public};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -75,8 +75,8 @@ impl ActivityHandler for BlockUserFromCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::send_lemmy_activity,
|
activities::send_lemmy_activity,
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
insert_activity,
|
insert_activity,
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
protocol::activities::community::announce::AnnounceActivity,
|
protocol::activities::community::announce::AnnounceActivity,
|
||||||
};
|
};
|
||||||
use lemmy_apub_lib::traits::ActorType;
|
use lemmy_apub_lib::{object_id::ObjectId, traits::ActorType};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
|
@ -9,7 +9,6 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::community::remove_mod::RemoveMod,
|
protocol::activities::community::remove_mod::RemoveMod,
|
||||||
|
@ -18,6 +17,7 @@ use activitystreams::{activity::kind::RemoveType, public};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -63,8 +63,8 @@ impl ActivityHandler for RemoveMod {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
|
use crate::{
|
||||||
|
activities::{
|
||||||
|
generate_activity_id,
|
||||||
|
send_lemmy_activity,
|
||||||
|
verify_activity,
|
||||||
|
verify_person_in_community,
|
||||||
|
},
|
||||||
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::community::report::Report,
|
||||||
|
PostOrComment,
|
||||||
|
};
|
||||||
use activitystreams::activity::kind::FlagType;
|
use activitystreams::activity::kind::FlagType;
|
||||||
|
|
||||||
use lemmy_api_common::{blocking, comment::CommentReportResponse, post::PostReportResponse};
|
use lemmy_api_common::{blocking, comment::CommentReportResponse, post::PostReportResponse};
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -16,19 +27,6 @@ use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::P
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
activities::{
|
|
||||||
generate_activity_id,
|
|
||||||
send_lemmy_activity,
|
|
||||||
verify_activity,
|
|
||||||
verify_person_in_community,
|
|
||||||
},
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
protocol::activities::community::report::Report,
|
|
||||||
PostOrComment,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Report {
|
impl Report {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
object_id: ObjectId<PostOrComment>,
|
object_id: ObjectId<PostOrComment>,
|
||||||
|
@ -72,7 +70,7 @@ impl ActivityHandler for Report {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.to[0].dereference(context, request_counter).await?;
|
let community = self.to[0].dereference(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -8,7 +8,6 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::community::{
|
protocol::activities::community::{
|
||||||
block_user::BlockUserFromCommunity,
|
block_user::BlockUserFromCommunity,
|
||||||
|
@ -19,6 +18,7 @@ use activitystreams::{activity::kind::UndoType, public};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -65,8 +65,8 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
|
|
|
@ -8,14 +8,14 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{activities::community::update::UpdateCommunity, objects::group::Group},
|
protocol::activities::community::update::UpdateCommunity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UpdateType, public};
|
use activitystreams::{activity::kind::UpdateType, public};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType, ApubObject},
|
traits::{ActivityHandler, ActorType, ApubObject},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -27,7 +27,7 @@ use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperati
|
||||||
|
|
||||||
impl UpdateCommunity {
|
impl UpdateCommunity {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
community: &ApubCommunity,
|
community: ApubCommunity,
|
||||||
actor: &ApubPerson,
|
actor: &ApubPerson,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
@ -38,15 +38,15 @@ impl UpdateCommunity {
|
||||||
let update = UpdateCommunity {
|
let update = UpdateCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: vec![public()],
|
to: vec![public()],
|
||||||
object: community.to_apub(context).await?,
|
object: Box::new(community.clone().into_apub(context).await?),
|
||||||
cc: vec![community.actor_id()],
|
cc: vec![community.actor_id()],
|
||||||
kind: UpdateType::Update,
|
kind: UpdateType::Update,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let activity = AnnouncableActivities::UpdateCommunity(Box::new(update));
|
let activity = AnnouncableActivities::UpdateCommunity(update);
|
||||||
send_to_community(activity, &id, actor, community, vec![], context).await
|
send_to_community(activity, &id, actor, &community, vec![], context).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,11 +58,18 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
|
ApubCommunity::verify(
|
||||||
|
&self.object,
|
||||||
|
&community.actor_id.clone().into(),
|
||||||
|
context,
|
||||||
|
request_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,12 +80,7 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
|
|
||||||
let updated_community = Group::from_apub_to_form(
|
let updated_community = self.object.into_form();
|
||||||
&self.object,
|
|
||||||
&community.actor_id.clone().into(),
|
|
||||||
&context.settings(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let cf = CommunityForm {
|
let cf = CommunityForm {
|
||||||
name: updated_community.name,
|
name: updated_community.name,
|
||||||
title: updated_community.title,
|
title: updated_community.title,
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
|
use crate::{
|
||||||
|
activities::{
|
||||||
|
community::{announce::GetCommunity, send_to_community},
|
||||||
|
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
||||||
|
generate_activity_id,
|
||||||
|
verify_activity,
|
||||||
|
verify_is_public,
|
||||||
|
},
|
||||||
|
activity_lists::AnnouncableActivities,
|
||||||
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::deletion::delete::Delete,
|
||||||
|
};
|
||||||
use activitystreams::{activity::kind::DeleteType, public};
|
use activitystreams::{activity::kind::DeleteType, public};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -29,20 +40,7 @@ use lemmy_websocket::{
|
||||||
LemmyContext,
|
LemmyContext,
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
use crate::{
|
|
||||||
activities::{
|
|
||||||
community::{announce::GetCommunity, send_to_community},
|
|
||||||
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
verify_is_public,
|
|
||||||
},
|
|
||||||
activity_lists::AnnouncableActivities,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
protocol::activities::deletion::delete::Delete,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ActivityHandler for Delete {
|
impl ActivityHandler for Delete {
|
||||||
|
@ -52,12 +50,12 @@ impl ActivityHandler for Delete {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object,
|
&self.object,
|
||||||
self,
|
&self.actor,
|
||||||
&community,
|
&community,
|
||||||
self.summary.is_some(),
|
self.summary.is_some(),
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use url::Url;
|
use crate::{
|
||||||
|
activities::{verify_mod_action, verify_person_in_community},
|
||||||
|
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
|
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
|
};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
traits::{ActivityFields, ActorType, ApubObject},
|
object_id::ObjectId,
|
||||||
|
traits::{ActorType, ApubObject},
|
||||||
verify::verify_domains_match,
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
||||||
|
@ -12,13 +16,7 @@ use lemmy_websocket::{
|
||||||
LemmyContext,
|
LemmyContext,
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
use crate::{
|
|
||||||
activities::{verify_mod_action, verify_person_in_community},
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
|
||||||
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod undo_delete;
|
pub mod undo_delete;
|
||||||
|
@ -55,9 +53,9 @@ pub async fn send_apub_remove(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DeletableObjects {
|
pub enum DeletableObjects {
|
||||||
Community(Box<ApubCommunity>),
|
Community(ApubCommunity),
|
||||||
Comment(Box<ApubComment>),
|
Comment(ApubComment),
|
||||||
Post(Box<ApubPost>),
|
Post(ApubPost),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeletableObjects {
|
impl DeletableObjects {
|
||||||
|
@ -66,13 +64,13 @@ impl DeletableObjects {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<DeletableObjects, LemmyError> {
|
) -> Result<DeletableObjects, LemmyError> {
|
||||||
if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
|
if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
|
||||||
return Ok(DeletableObjects::Community(Box::new(c)));
|
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_apub_id(ap_id.clone(), context).await? {
|
||||||
return Ok(DeletableObjects::Post(Box::new(p)));
|
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_apub_id(ap_id.clone(), context).await? {
|
||||||
return Ok(DeletableObjects::Comment(Box::new(c)));
|
return Ok(DeletableObjects::Comment(c));
|
||||||
}
|
}
|
||||||
Err(diesel::NotFound.into())
|
Err(diesel::NotFound.into())
|
||||||
}
|
}
|
||||||
|
@ -80,27 +78,26 @@ impl DeletableObjects {
|
||||||
|
|
||||||
pub(in crate::activities) async fn verify_delete_activity(
|
pub(in crate::activities) async fn verify_delete_activity(
|
||||||
object: &Url,
|
object: &Url,
|
||||||
activity: &dyn ActivityFields,
|
actor: &ObjectId<ApubPerson>,
|
||||||
community: &ApubCommunity,
|
community: &ApubCommunity,
|
||||||
is_mod_action: bool,
|
is_mod_action: bool,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let object = DeletableObjects::read_from_db(object, context).await?;
|
let object = DeletableObjects::read_from_db(object, context).await?;
|
||||||
let actor = ObjectId::new(activity.actor().clone());
|
|
||||||
match object {
|
match object {
|
||||||
DeletableObjects::Community(community) => {
|
DeletableObjects::Community(community) => {
|
||||||
if community.local {
|
if community.local {
|
||||||
// can only do this check for local community, in remote case it would try to fetch the
|
// can only do this check for local community, in remote case it would try to fetch the
|
||||||
// deleted community (which fails)
|
// deleted community (which fails)
|
||||||
verify_person_in_community(&actor, &community, context, request_counter).await?;
|
verify_person_in_community(actor, &community, context, request_counter).await?;
|
||||||
}
|
}
|
||||||
// community deletion is always a mod (or admin) action
|
// community deletion is always a mod (or admin) action
|
||||||
verify_mod_action(&actor, &community, context, request_counter).await?;
|
verify_mod_action(actor, &community, context, request_counter).await?;
|
||||||
}
|
}
|
||||||
DeletableObjects::Post(p) => {
|
DeletableObjects::Post(p) => {
|
||||||
verify_delete_activity_post_or_comment(
|
verify_delete_activity_post_or_comment(
|
||||||
activity,
|
actor,
|
||||||
&p.ap_id.clone().into(),
|
&p.ap_id.clone().into(),
|
||||||
community,
|
community,
|
||||||
is_mod_action,
|
is_mod_action,
|
||||||
|
@ -111,7 +108,7 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||||
}
|
}
|
||||||
DeletableObjects::Comment(c) => {
|
DeletableObjects::Comment(c) => {
|
||||||
verify_delete_activity_post_or_comment(
|
verify_delete_activity_post_or_comment(
|
||||||
activity,
|
actor,
|
||||||
&c.ap_id.clone().into(),
|
&c.ap_id.clone().into(),
|
||||||
community,
|
community,
|
||||||
is_mod_action,
|
is_mod_action,
|
||||||
|
@ -125,20 +122,19 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify_delete_activity_post_or_comment(
|
async fn verify_delete_activity_post_or_comment(
|
||||||
activity: &dyn ActivityFields,
|
actor: &ObjectId<ApubPerson>,
|
||||||
object_id: &Url,
|
object_id: &Url,
|
||||||
community: &ApubCommunity,
|
community: &ApubCommunity,
|
||||||
is_mod_action: bool,
|
is_mod_action: bool,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor = ObjectId::new(activity.actor().clone());
|
verify_person_in_community(actor, community, context, request_counter).await?;
|
||||||
verify_person_in_community(&actor, community, context, request_counter).await?;
|
|
||||||
if is_mod_action {
|
if is_mod_action {
|
||||||
verify_mod_action(&actor, community, context, request_counter).await?;
|
verify_mod_action(actor, community, context, request_counter).await?;
|
||||||
} else {
|
} else {
|
||||||
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
|
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
|
||||||
verify_domains_match(activity.actor(), object_id)?;
|
verify_domains_match(actor.inner(), object_id)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,3 @@
|
||||||
use activitystreams::{activity::kind::UndoType, public};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
|
||||||
use lemmy_apub_lib::{
|
|
||||||
data::Data,
|
|
||||||
traits::{ActivityHandler, ActorType},
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::{
|
|
||||||
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
|
|
||||||
LemmyContext,
|
|
||||||
UserOperationCrud,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::{announce::GetCommunity, send_to_community},
|
community::{announce::GetCommunity, send_to_community},
|
||||||
|
@ -24,10 +7,25 @@ use crate::{
|
||||||
verify_is_public,
|
verify_is_public,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
};
|
};
|
||||||
|
use activitystreams::{activity::kind::UndoType, public};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_apub_lib::{
|
||||||
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::{
|
||||||
|
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
|
||||||
|
LemmyContext,
|
||||||
|
UserOperationCrud,
|
||||||
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ActivityHandler for UndoDelete {
|
impl ActivityHandler for UndoDelete {
|
||||||
|
@ -37,13 +35,13 @@ impl ActivityHandler for UndoDelete {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object.object,
|
&self.object.object,
|
||||||
self,
|
&self.actor,
|
||||||
&community,
|
&community,
|
||||||
self.object.summary.is_some(),
|
self.object.summary.is_some(),
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_activity},
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity},
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
|
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::AcceptType;
|
use activitystreams::activity::kind::AcceptType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType},
|
||||||
verify::verify_urls_match,
|
verify::verify_urls_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
|
use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
|
||||||
|
@ -51,9 +51,9 @@ impl ActivityHandler for AcceptFollowCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
verify_urls_match(self.to[0].inner(), self.object.actor())?;
|
verify_urls_match(self.to[0].inner(), self.object.actor.inner())?;
|
||||||
verify_urls_match(self.actor(), self.object.to[0].inner())?;
|
verify_urls_match(self.actor.inner(), self.object.to[0].inner())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use crate::{
|
||||||
verify_person,
|
verify_person,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
|
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
|
||||||
};
|
};
|
||||||
|
@ -14,6 +13,7 @@ use activitystreams::activity::kind::FollowType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
verify::verify_urls_match,
|
verify::verify_urls_match,
|
||||||
};
|
};
|
||||||
|
@ -71,7 +71,7 @@ impl ActivityHandler for FollowCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
verify_urls_match(self.to[0].inner(), self.object.inner())?;
|
verify_urls_match(self.to[0].inner(), self.object.inner())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
let community = self.to[0].dereference(context, request_counter).await?;
|
let community = self.to[0].dereference(context, request_counter).await?;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
|
protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
|
||||||
};
|
};
|
||||||
|
@ -8,7 +7,8 @@ use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType},
|
||||||
verify::verify_urls_match,
|
verify::verify_urls_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -49,9 +49,9 @@ impl ActivityHandler for UndoFollowCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
verify_urls_match(self.to[0].inner(), self.object.object.inner())?;
|
verify_urls_match(self.to[0].inner(), self.object.object.inner())?;
|
||||||
verify_urls_match(self.actor(), self.object.actor())?;
|
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
context::WithContext,
|
context::WithContext,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
@ -11,7 +10,8 @@ use anyhow::anyhow;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
activity_queue::send_activity,
|
activity_queue::send_activity,
|
||||||
traits::{ActivityFields, ActorType},
|
object_id::ObjectId,
|
||||||
|
traits::ActorType,
|
||||||
verify::verify_domains_match,
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
|
@ -71,9 +71,9 @@ pub(crate) async fn verify_person_in_community(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_activity(activity: &dyn ActivityFields, settings: &Settings) -> Result<(), LemmyError> {
|
fn verify_activity(id: &Url, actor: &Url, settings: &Settings) -> Result<(), LemmyError> {
|
||||||
check_is_apub_id_valid(activity.actor(), false, settings)?;
|
check_is_apub_id_valid(actor, false, settings)?;
|
||||||
verify_domains_match(activity.id_unchecked(), activity.actor())?;
|
verify_domains_match(id, actor)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,14 +110,14 @@ fn verify_add_remove_moderator_target(
|
||||||
target: &Url,
|
target: &Url,
|
||||||
community: &ApubCommunity,
|
community: &ApubCommunity,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
if target != &generate_moderators_url(&community.actor_id)?.into_inner() {
|
if target != &generate_moderators_url(&community.actor_id)?.into() {
|
||||||
return Err(anyhow!("Unkown target url").into());
|
return Err(anyhow!("Unkown target url").into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify_is_public(to: &[Url]) -> Result<(), LemmyError> {
|
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> Result<(), LemmyError> {
|
||||||
if !to.contains(&public()) {
|
if !to.contains(&public()) && !cc.contains(&public()) {
|
||||||
return Err(anyhow!("Object is not public").into());
|
return Err(anyhow!("Object is not public").into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
use activitystreams::public;
|
|
||||||
use anyhow::anyhow;
|
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
|
||||||
use lemmy_apub_lib::{
|
|
||||||
data::Data,
|
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
|
|
||||||
verify::{verify_domains_match, verify_urls_match},
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{source::community::Community, traits::Crud};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
check_community_deleted_or_removed,
|
check_community_deleted_or_removed,
|
||||||
|
@ -22,14 +9,25 @@ use crate::{
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
|
protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
|
||||||
};
|
};
|
||||||
|
use activitystreams::public;
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_apub_lib::{
|
||||||
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType, ApubObject},
|
||||||
|
verify::{verify_domains_match, verify_urls_match},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::{source::community::Community, traits::Crud};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
||||||
|
|
||||||
impl CreateOrUpdatePost {
|
impl CreateOrUpdatePost {
|
||||||
pub(crate) async fn new(
|
pub(crate) async fn new(
|
||||||
post: &ApubPost,
|
post: ApubPost,
|
||||||
actor: &ApubPerson,
|
actor: &ApubPerson,
|
||||||
community: &ApubCommunity,
|
community: &ApubCommunity,
|
||||||
kind: CreateOrUpdateType,
|
kind: CreateOrUpdateType,
|
||||||
|
@ -42,7 +40,7 @@ impl CreateOrUpdatePost {
|
||||||
Ok(CreateOrUpdatePost {
|
Ok(CreateOrUpdatePost {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: vec![public()],
|
to: vec![public()],
|
||||||
object: post.to_apub(context).await?,
|
object: post.into_apub(context).await?,
|
||||||
cc: vec![community.actor_id()],
|
cc: vec![community.actor_id()],
|
||||||
kind,
|
kind,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
|
@ -50,7 +48,7 @@ impl CreateOrUpdatePost {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
post: &ApubPost,
|
post: ApubPost,
|
||||||
actor: &ApubPerson,
|
actor: &ApubPerson,
|
||||||
kind: CreateOrUpdateType,
|
kind: CreateOrUpdateType,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -63,7 +61,7 @@ impl CreateOrUpdatePost {
|
||||||
.into();
|
.into();
|
||||||
let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?;
|
let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?;
|
||||||
let id = create_or_update.id.clone();
|
let id = create_or_update.id.clone();
|
||||||
let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
|
let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update);
|
||||||
send_to_community(activity, &id, actor, &community, vec![], context).await
|
send_to_community(activity, &id, actor, &community, vec![], context).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,16 +74,16 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
check_community_deleted_or_removed(&community)?;
|
check_community_deleted_or_removed(&community)?;
|
||||||
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
CreateOrUpdateType::Create => {
|
CreateOrUpdateType::Create => {
|
||||||
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||||
verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
|
verify_urls_match(self.actor.inner(), self.object.attributed_to.inner())?;
|
||||||
// Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
|
// Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
|
||||||
// However, when fetching a remote post we generate a new create activity with the current
|
// However, when fetching a remote post we generate a new create activity with the current
|
||||||
// locked/stickied value, so this check may fail. So only check if its a local community,
|
// locked/stickied value, so this check may fail. So only check if its a local community,
|
||||||
|
@ -101,12 +99,12 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
if is_mod_action {
|
if is_mod_action {
|
||||||
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
} else {
|
} else {
|
||||||
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||||
verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
|
verify_urls_match(self.actor.inner(), self.object.attributed_to.inner())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.object.verify(context, request_counter).await?;
|
ApubPost::verify(&self.object, self.actor.inner(), context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,9 +113,7 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor = self.actor.dereference(context, request_counter).await?;
|
let post = ApubPost::from_apub(self.object, context, request_counter).await?;
|
||||||
let post =
|
|
||||||
ApubPost::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
|
|
||||||
|
|
||||||
let notif_type = match self.kind {
|
let notif_type = match self.kind {
|
||||||
CreateOrUpdateType::Create => UserOperationCrud::CreatePost,
|
CreateOrUpdateType::Create => UserOperationCrud::CreatePost,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||||
protocol::activities::{
|
protocol::activities::{
|
||||||
private_message::create_or_update::CreateOrUpdatePrivateMessage,
|
private_message::create_or_update::CreateOrUpdatePrivateMessage,
|
||||||
|
@ -10,6 +9,7 @@ use crate::{
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType, ApubObject},
|
traits::{ActivityHandler, ActorType, ApubObject},
|
||||||
verify::verify_domains_match,
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
|
@ -19,7 +19,7 @@ use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}
|
||||||
|
|
||||||
impl CreateOrUpdatePrivateMessage {
|
impl CreateOrUpdatePrivateMessage {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
private_message: &ApubPrivateMessage,
|
private_message: ApubPrivateMessage,
|
||||||
actor: &ApubPerson,
|
actor: &ApubPerson,
|
||||||
kind: CreateOrUpdateType,
|
kind: CreateOrUpdateType,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -38,7 +38,7 @@ impl CreateOrUpdatePrivateMessage {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [ObjectId::new(recipient.actor_id())],
|
to: [ObjectId::new(recipient.actor_id())],
|
||||||
object: private_message.to_apub(context).await?,
|
object: private_message.into_apub(context).await?,
|
||||||
kind,
|
kind,
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
@ -54,10 +54,10 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
ApubPrivateMessage::verify(&self.object, self.actor.inner(), context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +67,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let private_message =
|
let private_message =
|
||||||
ApubPrivateMessage::from_apub(&self.object, context, self.actor.inner(), request_counter)
|
ApubPrivateMessage::from_apub(self.object, context, request_counter).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
let notif_type = match self.kind {
|
let notif_type = match self.kind {
|
||||||
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
|
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||||
protocol::activities::private_message::delete::DeletePrivateMessage,
|
protocol::activities::private_message::delete::DeletePrivateMessage,
|
||||||
};
|
};
|
||||||
|
@ -8,6 +7,7 @@ use activitystreams::activity::kind::DeleteType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
verify::verify_domains_match,
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
|
@ -62,7 +62,7 @@ impl ActivityHandler for DeletePrivateMessage {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_domains_match(self.actor.inner(), self.object.inner())?;
|
verify_domains_match(self.actor.inner(), self.object.inner())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||||
protocol::activities::private_message::{
|
protocol::activities::private_message::{
|
||||||
delete::DeletePrivateMessage,
|
delete::DeletePrivateMessage,
|
||||||
|
@ -11,7 +10,8 @@ use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType},
|
||||||
verify::{verify_domains_match, verify_urls_match},
|
verify::{verify_domains_match, verify_urls_match},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -59,10 +59,10 @@ impl ActivityHandler for UndoDeletePrivateMessage {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_urls_match(self.actor(), self.object.actor())?;
|
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||||
verify_domains_match(self.actor(), self.object.object.inner())?;
|
verify_domains_match(self.actor.inner(), self.object.object.inner())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use activitystreams::{activity::kind::UndoType, public};
|
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
|
||||||
use lemmy_apub_lib::{
|
|
||||||
data::Data,
|
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
|
||||||
verify::verify_urls_match,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::{announce::GetCommunity, send_to_community},
|
community::{announce::GetCommunity, send_to_community},
|
||||||
|
@ -22,7 +8,6 @@ use crate::{
|
||||||
voting::{undo_vote_comment, undo_vote_post},
|
voting::{undo_vote_comment, undo_vote_post},
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::voting::{
|
protocol::activities::voting::{
|
||||||
undo_vote::UndoVote,
|
undo_vote::UndoVote,
|
||||||
|
@ -30,6 +15,17 @@ use crate::{
|
||||||
},
|
},
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
};
|
};
|
||||||
|
use activitystreams::{activity::kind::UndoType, public};
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_apub_lib::{
|
||||||
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType},
|
||||||
|
verify::verify_urls_match,
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
|
|
||||||
impl UndoVote {
|
impl UndoVote {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -72,11 +68,11 @@ impl ActivityHandler for UndoVote {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_urls_match(self.actor(), self.object.actor())?;
|
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -93,7 +89,7 @@ impl ActivityHandler for UndoVote {
|
||||||
.dereference(context, request_counter)
|
.dereference(context, request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
match object {
|
match object {
|
||||||
PostOrComment::Post(p) => undo_vote_post(actor, p.deref(), context).await,
|
PostOrComment::Post(p) => undo_vote_post(actor, &p, context).await,
|
||||||
PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await,
|
PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,3 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use activitystreams::public;
|
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
|
||||||
use lemmy_apub_lib::{
|
|
||||||
data::Data,
|
|
||||||
traits::{ActivityHandler, ActorType},
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
|
||||||
newtypes::CommunityId,
|
|
||||||
source::{community::Community, post::Post},
|
|
||||||
traits::Crud,
|
|
||||||
};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::{announce::GetCommunity, send_to_community},
|
community::{announce::GetCommunity, send_to_community},
|
||||||
|
@ -25,11 +8,24 @@ use crate::{
|
||||||
voting::{vote_comment, vote_post},
|
voting::{vote_comment, vote_post},
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::voting::vote::{Vote, VoteType},
|
protocol::activities::voting::vote::{Vote, VoteType},
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
};
|
};
|
||||||
|
use activitystreams::public;
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_apub_lib::{
|
||||||
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
|
traits::{ActivityHandler, ActorType},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
newtypes::CommunityId,
|
||||||
|
source::{community::Community, post::Post},
|
||||||
|
traits::Crud,
|
||||||
|
};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
|
|
||||||
impl Vote {
|
impl Vote {
|
||||||
pub(in crate::activities::voting) fn new(
|
pub(in crate::activities::voting) fn new(
|
||||||
|
@ -78,8 +74,8 @@ impl ActivityHandler for Vote {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_is_public(&self.to)?;
|
verify_is_public(&self.to, &self.cc)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
|
||||||
let community = self.get_community(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -93,7 +89,7 @@ impl ActivityHandler for Vote {
|
||||||
let actor = self.actor.dereference(context, request_counter).await?;
|
let actor = self.actor.dereference(context, request_counter).await?;
|
||||||
let object = self.object.dereference(context, request_counter).await?;
|
let object = self.object.dereference(context, request_counter).await?;
|
||||||
match object {
|
match object {
|
||||||
PostOrComment::Post(p) => vote_post(&self.kind, actor, p.deref(), context).await,
|
PostOrComment::Post(p) => vote_post(&self.kind, actor, &p, context).await,
|
||||||
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, &c, context).await,
|
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, &c, context).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,32 +26,32 @@ use crate::{
|
||||||
voting::{undo_vote::UndoVote, vote::Vote},
|
voting::{undo_vote::UndoVote, vote::Vote},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler};
|
use lemmy_apub_lib::traits::ActivityHandler;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
#[activity_handler(LemmyContext)]
|
#[activity_handler(LemmyContext)]
|
||||||
pub enum SharedInboxActivities {
|
pub enum SharedInboxActivities {
|
||||||
GroupInboxActivities(GroupInboxActivities),
|
GroupInboxActivities(GroupInboxActivities),
|
||||||
// Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
|
// 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.
|
// avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
|
||||||
PersonInboxActivities(PersonInboxActivities),
|
PersonInboxActivities(Box<PersonInboxActivities>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
#[activity_handler(LemmyContext)]
|
#[activity_handler(LemmyContext)]
|
||||||
pub enum GroupInboxActivities {
|
pub enum GroupInboxActivities {
|
||||||
FollowCommunity(FollowCommunity),
|
FollowCommunity(FollowCommunity),
|
||||||
UndoFollowCommunity(UndoFollowCommunity),
|
UndoFollowCommunity(UndoFollowCommunity),
|
||||||
AnnouncableActivities(AnnouncableActivities),
|
AnnouncableActivities(Box<AnnouncableActivities>),
|
||||||
Report(Report),
|
Report(Report),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
#[activity_handler(LemmyContext)]
|
#[activity_handler(LemmyContext)]
|
||||||
pub enum PersonInboxActivities {
|
pub enum PersonInboxActivities {
|
||||||
|
@ -61,20 +61,20 @@ pub enum PersonInboxActivities {
|
||||||
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
|
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
|
||||||
DeletePrivateMessage(DeletePrivateMessage),
|
DeletePrivateMessage(DeletePrivateMessage),
|
||||||
UndoDeletePrivateMessage(UndoDeletePrivateMessage),
|
UndoDeletePrivateMessage(UndoDeletePrivateMessage),
|
||||||
AnnounceActivity(Box<AnnounceActivity>),
|
AnnounceActivity(AnnounceActivity),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
#[activity_handler(LemmyContext)]
|
#[activity_handler(LemmyContext)]
|
||||||
pub enum AnnouncableActivities {
|
pub enum AnnouncableActivities {
|
||||||
CreateOrUpdateComment(CreateOrUpdateComment),
|
CreateOrUpdateComment(CreateOrUpdateComment),
|
||||||
CreateOrUpdatePost(Box<CreateOrUpdatePost>),
|
CreateOrUpdatePost(CreateOrUpdatePost),
|
||||||
Vote(Vote),
|
Vote(Vote),
|
||||||
UndoVote(UndoVote),
|
UndoVote(UndoVote),
|
||||||
Delete(Delete),
|
Delete(Delete),
|
||||||
UndoDelete(UndoDelete),
|
UndoDelete(UndoDelete),
|
||||||
UpdateCommunity(Box<UpdateCommunity>),
|
UpdateCommunity(UpdateCommunity),
|
||||||
BlockUserFromCommunity(BlockUserFromCommunity),
|
BlockUserFromCommunity(BlockUserFromCommunity),
|
||||||
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
||||||
AddMod(AddMod),
|
AddMod(AddMod),
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
collections::CommunityContext,
|
collections::CommunityContext,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::collections::group_moderators::GroupModerators,
|
protocol::collections::group_moderators::GroupModerators,
|
||||||
};
|
};
|
||||||
use activitystreams::{chrono::NaiveDateTime, collection::kind::OrderedCollectionType};
|
use activitystreams::{chrono::NaiveDateTime, collection::kind::OrderedCollectionType};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{traits::ApubObject, verify::verify_domains_match};
|
use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject, verify::verify_domains_match};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::community::{CommunityModerator, CommunityModeratorForm},
|
source::community::{CommunityModerator, CommunityModeratorForm},
|
||||||
traits::Joinable,
|
traits::Joinable,
|
||||||
|
@ -50,11 +49,11 @@ impl ApubObject for ApubCommunityModerators {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_apub(&self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||||
let ordered_items = self
|
let ordered_items = self
|
||||||
.0
|
.0
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|m| ObjectId::<ApubPerson>::new(m.moderator.actor_id.clone().into_inner()))
|
.map(|m| ObjectId::<ApubPerson>::new(m.moderator.actor_id))
|
||||||
.collect();
|
.collect();
|
||||||
Ok(GroupModerators {
|
Ok(GroupModerators {
|
||||||
r#type: OrderedCollectionType::OrderedCollection,
|
r#type: OrderedCollectionType::OrderedCollection,
|
||||||
|
@ -67,13 +66,21 @@ impl ApubObject for ApubCommunityModerators {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn from_apub(
|
async fn verify(
|
||||||
apub: &Self::ApubType,
|
group_moderators: &GroupModerators,
|
||||||
data: &Self::DataType,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
_context: &CommunityContext,
|
||||||
|
_request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_domains_match(&group_moderators.id, expected_domain)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_apub(
|
||||||
|
apub: Self::ApubType,
|
||||||
|
data: &Self::DataType,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<Self, LemmyError> {
|
) -> Result<Self, LemmyError> {
|
||||||
verify_domains_match(expected_domain, &apub.id)?;
|
|
||||||
let community_id = data.0.id;
|
let community_id = data.0.id;
|
||||||
let current_moderators = blocking(data.1.pool(), move |conn| {
|
let current_moderators = blocking(data.1.pool(), move |conn| {
|
||||||
CommunityModeratorView::for_community(conn, community_id)
|
CommunityModeratorView::for_community(conn, community_id)
|
||||||
|
@ -81,7 +88,7 @@ impl ApubObject for ApubCommunityModerators {
|
||||||
.await??;
|
.await??;
|
||||||
// Remove old mods from database which arent in the moderators collection anymore
|
// Remove old mods from database which arent in the moderators collection anymore
|
||||||
for mod_user in ¤t_moderators {
|
for mod_user in ¤t_moderators {
|
||||||
let mod_id = ObjectId::new(mod_user.moderator.actor_id.clone().into_inner());
|
let mod_id = ObjectId::new(mod_user.moderator.actor_id.clone());
|
||||||
if !apub.ordered_items.contains(&mod_id) {
|
if !apub.ordered_items.contains(&mod_id) {
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
community_id: mod_user.community.id,
|
community_id: mod_user.community.id,
|
||||||
|
@ -95,12 +102,11 @@ impl ApubObject for ApubCommunityModerators {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new mods to database which have been added to moderators collection
|
// Add new mods to database which have been added to moderators collection
|
||||||
for mod_id in &apub.ordered_items {
|
for mod_id in apub.ordered_items {
|
||||||
let mod_id = ObjectId::new(mod_id.clone());
|
let mod_id = ObjectId::new(mod_id);
|
||||||
let mod_user: ApubPerson = mod_id.dereference(&data.1, request_counter).await?;
|
let mod_user: ApubPerson = mod_id.dereference(&data.1, request_counter).await?;
|
||||||
|
|
||||||
if !current_moderators
|
if !current_moderators
|
||||||
.clone()
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.moderator.actor_id.clone())
|
.map(|c| c.moderator.actor_id.clone())
|
||||||
.any(|x| x == mod_user.actor_id)
|
.any(|x| x == mod_user.actor_id)
|
||||||
|
@ -167,7 +173,10 @@ mod tests {
|
||||||
0: community,
|
0: community,
|
||||||
1: context,
|
1: context,
|
||||||
};
|
};
|
||||||
ApubCommunityModerators::from_apub(&json, &community_context, &url, &mut request_counter)
|
ApubCommunityModerators::verify(&json, &url, &community_context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
ApubCommunityModerators::from_apub(json, &community_context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(request_counter, 0);
|
assert_eq!(request_counter, 0);
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
|
use crate::{
|
||||||
|
collections::CommunityContext,
|
||||||
|
generate_outbox_url,
|
||||||
|
objects::{person::ApubPerson, post::ApubPost},
|
||||||
|
protocol::{
|
||||||
|
activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
|
||||||
|
collections::group_outbox::GroupOutbox,
|
||||||
|
},
|
||||||
|
};
|
||||||
use activitystreams::collection::kind::OrderedCollectionType;
|
use activitystreams::collection::kind::OrderedCollectionType;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
@ -13,16 +20,7 @@ use lemmy_db_schema::{
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
use url::Url;
|
||||||
use crate::{
|
|
||||||
collections::CommunityContext,
|
|
||||||
generate_outbox_url,
|
|
||||||
objects::{person::ApubPerson, post::ApubPost},
|
|
||||||
protocol::{
|
|
||||||
activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
|
|
||||||
collections::group_outbox::GroupOutbox,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
|
pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
|
||||||
|
@ -62,9 +60,9 @@ impl ApubObject for ApubCommunityOutbox {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_apub(&self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||||
let mut ordered_items = vec![];
|
let mut ordered_items = vec![];
|
||||||
for post in &self.0 {
|
for post in self.0 {
|
||||||
let actor = post.creator_id;
|
let actor = post.creator_id;
|
||||||
let actor: ApubPerson = blocking(data.1.pool(), move |conn| Person::read(conn, actor))
|
let actor: ApubPerson = blocking(data.1.pool(), move |conn| Person::read(conn, actor))
|
||||||
.await??
|
.await??
|
||||||
|
@ -87,14 +85,22 @@ impl ApubObject for ApubCommunityOutbox {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn from_apub(
|
async fn verify(
|
||||||
apub: &Self::ApubType,
|
group_outbox: &GroupOutbox,
|
||||||
data: &Self::DataType,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
_context: &CommunityContext,
|
||||||
|
_request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_domains_match(expected_domain, &group_outbox.id)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_apub(
|
||||||
|
apub: Self::ApubType,
|
||||||
|
data: &Self::DataType,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<Self, LemmyError> {
|
) -> Result<Self, LemmyError> {
|
||||||
verify_domains_match(expected_domain, &apub.id)?;
|
let mut outbox_activities = apub.ordered_items;
|
||||||
let mut outbox_activities = apub.ordered_items.clone();
|
|
||||||
if outbox_activities.len() > 20 {
|
if outbox_activities.len() > 20 {
|
||||||
outbox_activities = outbox_activities[0..20].to_vec();
|
outbox_activities = outbox_activities[0..20].to_vec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,9 @@
|
||||||
use activitystreams::{base::AnyBase, context, primitives::OneOrMany};
|
use activitystreams::{base::AnyBase, primitives::OneOrMany};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CONTEXT: OneOrMany<AnyBase> = {
|
static ref CONTEXT: OneOrMany<AnyBase> =
|
||||||
let context_ext = AnyBase::from_arbitrary_json(json!(
|
serde_json::from_str(include_str!("../assets/lemmy/context.json")).expect("parse context");
|
||||||
{
|
|
||||||
"sc": "http://schema.org#",
|
|
||||||
"sensitive": "as:sensitive",
|
|
||||||
"stickied": "as:stickied",
|
|
||||||
"pt": "https://join-lemmy.org#",
|
|
||||||
"comments_enabled": {
|
|
||||||
"type": "sc:Boolean",
|
|
||||||
"id": "pt:commentsEnabled"
|
|
||||||
},
|
|
||||||
"moderators": "as:moderators",
|
|
||||||
"matrixUserId": {
|
|
||||||
"type": "sc:Text",
|
|
||||||
"id": "as:alsoKnownAs"
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
.expect("parse context");
|
|
||||||
OneOrMany::from(vec![
|
|
||||||
AnyBase::from(context()),
|
|
||||||
context_ext,
|
|
||||||
AnyBase::from(Url::parse("https://w3id.org/security/v1").expect("parse context")),
|
|
||||||
])
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,56 +1,3 @@
|
||||||
pub mod object_id;
|
|
||||||
pub mod post_or_comment;
|
pub mod post_or_comment;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
|
pub mod user_or_community;
|
||||||
use crate::{
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
};
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
use lemmy_apub_lib::traits::ActorType;
|
|
||||||
use lemmy_db_schema::naive_now;
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
static ACTOR_REFETCH_INTERVAL_SECONDS: i64 = 24 * 60 * 60;
|
|
||||||
static ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG: i64 = 10;
|
|
||||||
|
|
||||||
/// Get a remote actor from its apub ID (either a person or a community). Thin wrapper around
|
|
||||||
/// `get_or_fetch_and_upsert_person()` and `get_or_fetch_and_upsert_community()`.
|
|
||||||
///
|
|
||||||
/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
|
|
||||||
/// Otherwise it is fetched from the remote instance, stored and returned.
|
|
||||||
pub(crate) async fn get_or_fetch_and_upsert_actor(
|
|
||||||
apub_id: Url,
|
|
||||||
context: &LemmyContext,
|
|
||||||
recursion_counter: &mut i32,
|
|
||||||
) -> Result<Box<dyn ActorType>, LemmyError> {
|
|
||||||
let community_id = ObjectId::<ApubCommunity>::new(apub_id.clone());
|
|
||||||
let community = community_id.dereference(context, recursion_counter).await;
|
|
||||||
let actor: Box<dyn ActorType> = match community {
|
|
||||||
Ok(c) => Box::new(c),
|
|
||||||
Err(_) => {
|
|
||||||
let person_id = ObjectId::new(apub_id);
|
|
||||||
let person: ApubPerson = person_id.dereference(context, recursion_counter).await?;
|
|
||||||
Box::new(person)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(actor)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines when a remote actor should be refetched from its instance. In release builds, this is
|
|
||||||
/// `ACTOR_REFETCH_INTERVAL_SECONDS` after the last refetch, in debug builds
|
|
||||||
/// `ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG`.
|
|
||||||
///
|
|
||||||
/// TODO it won't pick up new avatars, summaries etc until a day after.
|
|
||||||
/// Actors need an "update" activity pushed to other servers to fix this.
|
|
||||||
fn should_refetch_object(last_refreshed: NaiveDateTime) -> bool {
|
|
||||||
let update_interval = if cfg!(debug_assertions) {
|
|
||||||
// avoid infinite loop when fetching community outbox
|
|
||||||
chrono::Duration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG)
|
|
||||||
} else {
|
|
||||||
chrono::Duration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS)
|
|
||||||
};
|
|
||||||
last_refreshed.lt(&(naive_now() - update_interval))
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,33 +1,25 @@
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use lemmy_apub_lib::traits::ApubObject;
|
|
||||||
use lemmy_db_schema::source::{comment::CommentForm, post::PostForm};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{comment::ApubComment, post::ApubPost},
|
objects::{comment::ApubComment, post::ApubPost},
|
||||||
protocol::objects::{note::Note, page::Page},
|
protocol::objects::{note::Note, page::Page},
|
||||||
};
|
};
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use lemmy_apub_lib::traits::ApubObject;
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PostOrComment {
|
pub enum PostOrComment {
|
||||||
Post(Box<ApubPost>),
|
Post(ApubPost),
|
||||||
Comment(ApubComment),
|
Comment(ApubComment),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PostOrCommentForm {
|
|
||||||
PostForm(Box<PostForm>),
|
|
||||||
CommentForm(CommentForm),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum PageOrNote {
|
pub enum PageOrNote {
|
||||||
Page(Box<Page>),
|
Page(Page),
|
||||||
Note(Box<Note>),
|
Note(Note),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -44,13 +36,10 @@ impl ApubObject for PostOrComment {
|
||||||
async fn read_from_apub_id(
|
async fn read_from_apub_id(
|
||||||
object_id: Url,
|
object_id: Url,
|
||||||
data: &Self::DataType,
|
data: &Self::DataType,
|
||||||
) -> Result<Option<Self>, LemmyError>
|
) -> Result<Option<Self>, LemmyError> {
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?;
|
let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?;
|
||||||
Ok(match post {
|
Ok(match post {
|
||||||
Some(o) => Some(PostOrComment::Post(Box::new(o))),
|
Some(o) => Some(PostOrComment::Post(o)),
|
||||||
None => ApubComment::read_from_apub_id(object_id, data)
|
None => ApubComment::read_from_apub_id(object_id, data)
|
||||||
.await?
|
.await?
|
||||||
.map(PostOrComment::Comment),
|
.map(PostOrComment::Comment),
|
||||||
|
@ -64,7 +53,7 @@ impl ApubObject for PostOrComment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_apub(&self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,22 +61,30 @@ impl ApubObject for PostOrComment {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn from_apub(
|
async fn verify(
|
||||||
apub: &PageOrNote,
|
apub: &Self::ApubType,
|
||||||
context: &LemmyContext,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
data: &Self::DataType,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<Self, LemmyError>
|
) -> Result<(), LemmyError> {
|
||||||
where
|
match apub {
|
||||||
Self: Sized,
|
PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data, request_counter).await,
|
||||||
{
|
PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data, request_counter).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_apub(
|
||||||
|
apub: PageOrNote,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<Self, LemmyError> {
|
||||||
Ok(match apub {
|
Ok(match apub {
|
||||||
PageOrNote::Page(p) => PostOrComment::Post(Box::new(
|
PageOrNote::Page(p) => {
|
||||||
ApubPost::from_apub(p, context, expected_domain, request_counter).await?,
|
PostOrComment::Post(ApubPost::from_apub(p, context, request_counter).await?)
|
||||||
)),
|
}
|
||||||
PageOrNote::Note(n) => PostOrComment::Comment(
|
PageOrNote::Note(n) => {
|
||||||
ApubComment::from_apub(n, context, expected_domain, request_counter).await?,
|
PostOrComment::Comment(ApubComment::from_apub(n, context, request_counter).await?)
|
||||||
),
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
use crate::{
|
||||||
|
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
|
protocol::objects::{group::Group, note::Note, page::Page, person::Person},
|
||||||
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::Deserialize;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
|
object_id::ObjectId,
|
||||||
traits::ApubObject,
|
traits::ApubObject,
|
||||||
webfinger::{webfinger_resolve_actor, WebfingerType},
|
webfinger::{webfinger_resolve_actor, WebfingerType},
|
||||||
};
|
};
|
||||||
|
@ -15,12 +17,8 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use serde::Deserialize;
|
||||||
use crate::{
|
use url::Url;
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
|
||||||
protocol::objects::{group::Group, note::Note, page::Page, person::Person},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
|
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
|
||||||
///
|
///
|
||||||
|
@ -157,7 +155,7 @@ impl ApubObject for SearchableObjects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_apub(&self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,19 +163,40 @@ impl ApubObject for SearchableObjects {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn from_apub(
|
async fn verify(
|
||||||
apub: &Self::ApubType,
|
apub: &Self::ApubType,
|
||||||
|
expected_domain: &Url,
|
||||||
|
data: &Self::DataType,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_apub(
|
||||||
|
apub: Self::ApubType,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
ed: &Url,
|
|
||||||
rc: &mut i32,
|
rc: &mut i32,
|
||||||
) -> Result<Self, LemmyError> {
|
) -> Result<Self, LemmyError> {
|
||||||
use SearchableApubTypes as SAT;
|
use SearchableApubTypes as SAT;
|
||||||
use SearchableObjects as SO;
|
use SearchableObjects as SO;
|
||||||
Ok(match apub {
|
Ok(match apub {
|
||||||
SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, ed, rc).await?),
|
SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, rc).await?),
|
||||||
SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, ed, rc).await?),
|
SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, rc).await?),
|
||||||
SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, ed, rc).await?),
|
SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, rc).await?),
|
||||||
SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, ed, rc).await?),
|
SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, rc).await?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
120
crates/apub/src/fetcher/user_or_community.rs
Normal file
120
crates/apub/src/fetcher/user_or_community.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use crate::{
|
||||||
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::objects::{group::Group, person::Person},
|
||||||
|
};
|
||||||
|
use activitystreams::{chrono::NaiveDateTime, url::Url};
|
||||||
|
use lemmy_apub_lib::traits::{ActorType, ApubObject};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum UserOrCommunity {
|
||||||
|
User(ApubPerson),
|
||||||
|
Community(ApubCommunity),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PersonOrGroup {
|
||||||
|
Person(Person),
|
||||||
|
Group(Group),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl ApubObject for UserOrCommunity {
|
||||||
|
type DataType = LemmyContext;
|
||||||
|
type ApubType = PersonOrGroup;
|
||||||
|
type TombstoneType = ();
|
||||||
|
|
||||||
|
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
|
||||||
|
Some(match self {
|
||||||
|
UserOrCommunity::User(p) => p.last_refreshed_at,
|
||||||
|
UserOrCommunity::Community(p) => p.last_refreshed_at,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_from_apub_id(
|
||||||
|
object_id: Url,
|
||||||
|
data: &Self::DataType,
|
||||||
|
) -> Result<Option<Self>, LemmyError> {
|
||||||
|
let person = ApubPerson::read_from_apub_id(object_id.clone(), data).await?;
|
||||||
|
Ok(match person {
|
||||||
|
Some(o) => Some(UserOrCommunity::User(o)),
|
||||||
|
None => ApubCommunity::read_from_apub_id(object_id, data)
|
||||||
|
.await?
|
||||||
|
.map(UserOrCommunity::Community),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(self, 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> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn verify(
|
||||||
|
apub: &Self::ApubType,
|
||||||
|
expected_domain: &Url,
|
||||||
|
data: &Self::DataType,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_apub(
|
||||||
|
apub: Self::ApubType,
|
||||||
|
data: &Self::DataType,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<Self, LemmyError> {
|
||||||
|
Ok(match apub {
|
||||||
|
PersonOrGroup::Person(p) => {
|
||||||
|
UserOrCommunity::User(ApubPerson::from_apub(p, data, request_counter).await?)
|
||||||
|
}
|
||||||
|
PersonOrGroup::Group(p) => {
|
||||||
|
UserOrCommunity::Community(ApubCommunity::from_apub(p, data, request_counter).await?)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActorType for UserOrCommunity {
|
||||||
|
fn actor_id(&self) -> Url {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_key(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
UserOrCommunity::User(p) => p.public_key(),
|
||||||
|
UserOrCommunity::Community(p) => p.public_key(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn private_key(&self) -> Option<String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inbox_url(&self) -> Url {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shared_inbox_url(&self) -> Option<Url> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ pub(crate) async fn get_apub_comment(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !comment.deleted {
|
if !comment.deleted {
|
||||||
Ok(create_apub_response(&comment.to_apub(&**context).await?))
|
Ok(create_apub_response(&comment.into_apub(&**context).await?))
|
||||||
} else {
|
} else {
|
||||||
Ok(create_apub_tombstone_response(&comment.to_tombstone()?))
|
Ok(create_apub_tombstone_response(&comment.to_tombstone()?))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,13 @@ use crate::{
|
||||||
CommunityContext,
|
CommunityContext,
|
||||||
},
|
},
|
||||||
context::WithContext,
|
context::WithContext,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
generate_outbox_url,
|
generate_outbox_url,
|
||||||
http::{
|
http::{
|
||||||
create_apub_response,
|
create_apub_response,
|
||||||
create_apub_tombstone_response,
|
create_apub_tombstone_response,
|
||||||
payload_to_string,
|
payload_to_string,
|
||||||
receive_activity,
|
receive_activity,
|
||||||
|
ActivityCommonFields,
|
||||||
},
|
},
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
protocol::{
|
protocol::{
|
||||||
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
|
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::traits::{ActivityFields, ApubObject};
|
use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
@ -47,7 +47,7 @@ pub(crate) async fn get_apub_community_http(
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
if !community.deleted {
|
if !community.deleted {
|
||||||
let apub = community.to_apub(&**context).await?;
|
let apub = community.into_apub(&**context).await?;
|
||||||
|
|
||||||
Ok(create_apub_response(&apub))
|
Ok(create_apub_response(&apub))
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,26 +64,28 @@ pub async fn community_inbox(
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let unparsed = payload_to_string(payload).await?;
|
let unparsed = payload_to_string(payload).await?;
|
||||||
info!("Received community inbox activity {}", unparsed);
|
info!("Received community inbox activity {}", unparsed);
|
||||||
|
let activity_data: ActivityCommonFields = serde_json::from_str(&unparsed)?;
|
||||||
let activity = serde_json::from_str::<WithContext<GroupInboxActivities>>(&unparsed)?;
|
let activity = serde_json::from_str::<WithContext<GroupInboxActivities>>(&unparsed)?;
|
||||||
|
|
||||||
receive_group_inbox(activity.inner(), request, &context).await?;
|
receive_group_inbox(activity.inner(), activity_data, request, &context).await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::http) async fn receive_group_inbox(
|
pub(in crate::http) async fn receive_group_inbox(
|
||||||
activity: GroupInboxActivities,
|
activity: GroupInboxActivities,
|
||||||
|
activity_data: ActivityCommonFields,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let res = receive_activity(request, activity.clone(), context).await;
|
let actor_id = ObjectId::new(activity_data.actor.clone());
|
||||||
|
let res = receive_activity(request, activity.clone(), activity_data, context).await;
|
||||||
|
|
||||||
if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
|
if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
|
||||||
let community = announcable.get_community(context, &mut 0).await?;
|
let community = announcable.get_community(context, &mut 0).await?;
|
||||||
let actor_id = ObjectId::new(announcable.actor().clone());
|
|
||||||
verify_person_in_community(&actor_id, &community, context, &mut 0).await?;
|
verify_person_in_community(&actor_id, &community, context, &mut 0).await?;
|
||||||
if community.local {
|
if community.local {
|
||||||
AnnounceActivity::send(announcable, &community, vec![], context).await?;
|
AnnounceActivity::send(*announcable, &community, vec![], context).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,10 +115,10 @@ pub(crate) async fn get_apub_community_outbox(
|
||||||
Community::read_from_name(conn, &info.community_name)
|
Community::read_from_name(conn, &info.community_name)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
let id = ObjectId::new(generate_outbox_url(&community.actor_id)?.into_inner());
|
let id = ObjectId::new(generate_outbox_url(&community.actor_id)?);
|
||||||
let outbox_data = CommunityContext(community.into(), context.get_ref().clone());
|
let outbox_data = CommunityContext(community.into(), context.get_ref().clone());
|
||||||
let outbox: ApubCommunityOutbox = id.dereference(&outbox_data, &mut 0).await?;
|
let outbox: ApubCommunityOutbox = id.dereference(&outbox_data, &mut 0).await?;
|
||||||
Ok(create_apub_response(&outbox.to_apub(&outbox_data).await?))
|
Ok(create_apub_response(&outbox.into_apub(&outbox_data).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_apub_community_moderators(
|
pub(crate) async fn get_apub_community_moderators(
|
||||||
|
@ -128,10 +130,10 @@ pub(crate) async fn get_apub_community_moderators(
|
||||||
})
|
})
|
||||||
.await??
|
.await??
|
||||||
.into();
|
.into();
|
||||||
let id = ObjectId::new(generate_outbox_url(&community.actor_id)?.into_inner());
|
let id = ObjectId::new(generate_outbox_url(&community.actor_id)?);
|
||||||
let outbox_data = CommunityContext(community, context.get_ref().clone());
|
let outbox_data = CommunityContext(community, context.get_ref().clone());
|
||||||
let moderators: ApubCommunityModerators = id.dereference(&outbox_data, &mut 0).await?;
|
let moderators: ApubCommunityModerators = id.dereference(&outbox_data, &mut 0).await?;
|
||||||
Ok(create_apub_response(
|
Ok(create_apub_response(
|
||||||
&moderators.to_apub(&outbox_data).await?,
|
&moderators.into_apub(&outbox_data).await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
activity_lists::SharedInboxActivities,
|
activity_lists::SharedInboxActivities,
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
context::WithContext,
|
context::WithContext,
|
||||||
fetcher::get_or_fetch_and_upsert_actor,
|
fetcher::user_or_community::UserOrCommunity,
|
||||||
http::{community::receive_group_inbox, person::receive_person_inbox},
|
http::{community::receive_group_inbox, person::receive_person_inbox},
|
||||||
insert_activity,
|
insert_activity,
|
||||||
};
|
};
|
||||||
|
@ -19,8 +19,9 @@ use http::StatusCode;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
object_id::ObjectId,
|
||||||
signatures::verify_signature,
|
signatures::verify_signature,
|
||||||
traits::{ActivityFields, ActivityHandler},
|
traits::{ActivityHandler, ActorType},
|
||||||
APUB_JSON_CONTENT_TYPE,
|
APUB_JSON_CONTENT_TYPE,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{source::activity::Activity, DbPool};
|
use lemmy_db_schema::{source::activity::Activity, DbPool};
|
||||||
|
@ -44,13 +45,14 @@ pub async fn shared_inbox(
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let unparsed = payload_to_string(payload).await?;
|
let unparsed = payload_to_string(payload).await?;
|
||||||
info!("Received shared inbox activity {}", unparsed);
|
info!("Received shared inbox activity {}", unparsed);
|
||||||
|
let activity_data: ActivityCommonFields = serde_json::from_str(&unparsed)?;
|
||||||
let activity = serde_json::from_str::<WithContext<SharedInboxActivities>>(&unparsed)?;
|
let activity = serde_json::from_str::<WithContext<SharedInboxActivities>>(&unparsed)?;
|
||||||
match activity.inner() {
|
match activity.inner() {
|
||||||
SharedInboxActivities::GroupInboxActivities(g) => {
|
SharedInboxActivities::GroupInboxActivities(g) => {
|
||||||
receive_group_inbox(g, request, &context).await
|
receive_group_inbox(g, activity_data, request, &context).await
|
||||||
}
|
}
|
||||||
SharedInboxActivities::PersonInboxActivities(p) => {
|
SharedInboxActivities::PersonInboxActivities(p) => {
|
||||||
receive_person_inbox(p, request, &context).await
|
receive_person_inbox(*p, activity_data, request, &context).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,15 +67,22 @@ async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
|
||||||
Ok(unparsed)
|
Ok(unparsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub(crate) struct ActivityCommonFields {
|
||||||
|
pub(crate) id: Url,
|
||||||
|
pub(crate) actor: Url,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: move most of this code to library
|
// TODO: move most of this code to library
|
||||||
async fn receive_activity<'a, T>(
|
async fn receive_activity<'a, T>(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
activity: T,
|
activity: T,
|
||||||
|
activity_data: ActivityCommonFields,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<HttpResponse, LemmyError>
|
) -> Result<HttpResponse, LemmyError>
|
||||||
where
|
where
|
||||||
T: ActivityHandler<DataType = LemmyContext>
|
T: ActivityHandler<DataType = LemmyContext>
|
||||||
+ ActivityFields
|
|
||||||
+ Clone
|
+ Clone
|
||||||
+ Deserialize<'a>
|
+ Deserialize<'a>
|
||||||
+ Serialize
|
+ Serialize
|
||||||
|
@ -81,26 +90,27 @@ where
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
|
check_is_apub_id_valid(&activity_data.actor, false, &context.settings())?;
|
||||||
let request_counter = &mut 0;
|
let request_counter = &mut 0;
|
||||||
let actor =
|
let actor = ObjectId::<UserOrCommunity>::new(activity_data.actor)
|
||||||
get_or_fetch_and_upsert_actor(activity.actor().clone(), context, request_counter).await?;
|
.dereference(context, request_counter)
|
||||||
|
.await?;
|
||||||
verify_signature(&request, &actor.public_key().context(location_info!())?)?;
|
verify_signature(&request, &actor.public_key().context(location_info!())?)?;
|
||||||
|
|
||||||
// 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.id_unchecked()).await? {
|
if is_activity_already_known(context.pool(), &activity_data.id).await? {
|
||||||
return Ok(HttpResponse::Ok().finish());
|
return Ok(HttpResponse::Ok().finish());
|
||||||
}
|
}
|
||||||
check_is_apub_id_valid(activity.actor(), false, &context.settings())?;
|
info!("Verifying activity {}", activity_data.id.to_string());
|
||||||
info!("Verifying activity {}", activity.id_unchecked().to_string());
|
|
||||||
activity
|
activity
|
||||||
.verify(&Data::new(context.clone()), request_counter)
|
.verify(&Data::new(context.clone()), request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
assert_activity_not_local(&activity, &context.settings().hostname)?;
|
assert_activity_not_local(&activity_data.id, &context.settings().hostname)?;
|
||||||
|
|
||||||
// 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.
|
||||||
insert_activity(
|
insert_activity(
|
||||||
activity.id_unchecked(),
|
&activity_data.id,
|
||||||
activity.clone(),
|
activity.clone(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
@ -108,7 +118,7 @@ where
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
info!("Receiving activity {}", activity.id_unchecked().to_string());
|
info!("Receiving activity {}", activity_data.id.to_string());
|
||||||
activity
|
activity
|
||||||
.receive(&Data::new(context.clone()), request_counter)
|
.receive(&Data::new(context.clone()), request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -183,17 +193,14 @@ pub(crate) async fn is_activity_already_known(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_activity_not_local<T: Debug + ActivityFields>(
|
fn assert_activity_not_local(id: &Url, hostname: &str) -> Result<(), LemmyError> {
|
||||||
activity: &T,
|
let activity_domain = id.domain().context(location_info!())?;
|
||||||
hostname: &str,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let activity_domain = activity.id_unchecked().domain().context(location_info!())?;
|
|
||||||
|
|
||||||
if activity_domain == hostname {
|
if activity_domain == hostname {
|
||||||
return Err(
|
return Err(
|
||||||
anyhow!(
|
anyhow!(
|
||||||
"Error: received activity which was sent by local instance: {:?}",
|
"Error: received activity which was sent by local instance: {:?}",
|
||||||
activity
|
id
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
||||||
create_apub_tombstone_response,
|
create_apub_tombstone_response,
|
||||||
payload_to_string,
|
payload_to_string,
|
||||||
receive_activity,
|
receive_activity,
|
||||||
|
ActivityCommonFields,
|
||||||
},
|
},
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::collections::person_outbox::PersonOutbox,
|
protocol::collections::person_outbox::PersonOutbox,
|
||||||
|
@ -38,7 +39,7 @@ pub(crate) async fn get_apub_person_http(
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
if !person.deleted {
|
if !person.deleted {
|
||||||
let apub = person.to_apub(&context).await?;
|
let apub = person.into_apub(&context).await?;
|
||||||
|
|
||||||
Ok(create_apub_response(&apub))
|
Ok(create_apub_response(&apub))
|
||||||
} else {
|
} else {
|
||||||
|
@ -54,16 +55,18 @@ pub async fn person_inbox(
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let unparsed = payload_to_string(payload).await?;
|
let unparsed = payload_to_string(payload).await?;
|
||||||
info!("Received person inbox activity {}", unparsed);
|
info!("Received person inbox activity {}", unparsed);
|
||||||
|
let activity_data: ActivityCommonFields = serde_json::from_str(&unparsed)?;
|
||||||
let activity = serde_json::from_str::<WithContext<PersonInboxActivities>>(&unparsed)?;
|
let activity = serde_json::from_str::<WithContext<PersonInboxActivities>>(&unparsed)?;
|
||||||
receive_person_inbox(activity.inner(), request, &context).await
|
receive_person_inbox(activity.inner(), activity_data, request, &context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::http) async fn receive_person_inbox(
|
pub(in crate::http) async fn receive_person_inbox(
|
||||||
activity: PersonInboxActivities,
|
activity: PersonInboxActivities,
|
||||||
|
activity_data: ActivityCommonFields,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
receive_activity(request, activity, context).await
|
receive_activity(request, activity, activity_data, context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_apub_person_outbox(
|
pub(crate) async fn get_apub_person_outbox(
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub(crate) async fn get_apub_post(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !post.deleted {
|
if !post.deleted {
|
||||||
Ok(create_apub_response(&post.to_apub(&context).await?))
|
Ok(create_apub_response(&post.into_apub(&context).await?))
|
||||||
} else {
|
} else {
|
||||||
Ok(create_apub_tombstone_response(&post.to_tombstone()?))
|
Ok(create_apub_tombstone_response(&post.to_tombstone()?))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
use std::ops::Deref;
|
use crate::{
|
||||||
|
activities::{verify_is_public, verify_person_in_community},
|
||||||
|
check_is_apub_id_valid,
|
||||||
|
protocol::{
|
||||||
|
objects::{
|
||||||
|
note::{Note, SourceCompat},
|
||||||
|
tombstone::Tombstone,
|
||||||
|
},
|
||||||
|
Source,
|
||||||
|
},
|
||||||
|
PostOrComment,
|
||||||
|
};
|
||||||
use activitystreams::{object::kind::NoteType, public};
|
use activitystreams::{object::kind::NoteType, public};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use html2md::parse_html;
|
use html2md::parse_html;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
|
object_id::ObjectId,
|
||||||
traits::ApubObject,
|
traits::ApubObject,
|
||||||
values::{MediaTypeHtml, MediaTypeMarkdown},
|
values::{MediaTypeHtml, MediaTypeMarkdown},
|
||||||
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -21,25 +31,12 @@ use lemmy_db_schema::{
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
utils::{convert_datetime, remove_slurs},
|
utils::{convert_datetime, markdown_to_html, remove_slurs},
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use std::ops::Deref;
|
||||||
use crate::{
|
use url::Url;
|
||||||
activities::verify_person_in_community,
|
|
||||||
check_is_apub_id_valid,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
protocol::{
|
|
||||||
objects::{
|
|
||||||
note::{Note, SourceCompat},
|
|
||||||
tombstone::Tombstone,
|
|
||||||
},
|
|
||||||
Source,
|
|
||||||
},
|
|
||||||
PostOrComment,
|
|
||||||
};
|
|
||||||
use lemmy_utils::utils::markdown_to_html;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ApubComment(Comment);
|
pub struct ApubComment(Comment);
|
||||||
|
@ -90,7 +87,7 @@ impl ApubObject for ApubComment {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_apub(&self, context: &LemmyContext) -> Result<Note, LemmyError> {
|
async fn into_apub(self, context: &LemmyContext) -> Result<Note, LemmyError> {
|
||||||
let creator_id = self.creator_id;
|
let creator_id = self.creator_id;
|
||||||
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
|
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
|
||||||
|
|
||||||
|
@ -100,16 +97,17 @@ impl ApubObject for ApubComment {
|
||||||
let in_reply_to = if let Some(comment_id) = self.parent_id {
|
let in_reply_to = if let Some(comment_id) = self.parent_id {
|
||||||
let parent_comment =
|
let parent_comment =
|
||||||
blocking(context.pool(), move |conn| Comment::read(conn, comment_id)).await??;
|
blocking(context.pool(), move |conn| Comment::read(conn, comment_id)).await??;
|
||||||
ObjectId::<PostOrComment>::new(parent_comment.ap_id.into_inner())
|
ObjectId::<PostOrComment>::new(parent_comment.ap_id)
|
||||||
} else {
|
} else {
|
||||||
ObjectId::<PostOrComment>::new(post.ap_id.into_inner())
|
ObjectId::<PostOrComment>::new(post.ap_id)
|
||||||
};
|
};
|
||||||
|
|
||||||
let note = Note {
|
let note = Note {
|
||||||
r#type: NoteType::Note,
|
r#type: NoteType::Note,
|
||||||
id: self.ap_id.to_owned().into_inner(),
|
id: ObjectId::new(self.ap_id.clone()),
|
||||||
attributed_to: ObjectId::new(creator.actor_id),
|
attributed_to: ObjectId::new(creator.actor_id),
|
||||||
to: vec![public()],
|
to: vec![public()],
|
||||||
|
cc: vec![],
|
||||||
content: markdown_to_html(&self.content),
|
content: markdown_to_html(&self.content),
|
||||||
media_type: Some(MediaTypeHtml::Html),
|
media_type: Some(MediaTypeHtml::Html),
|
||||||
source: SourceCompat::Lemmy(Source {
|
source: SourceCompat::Lemmy(Source {
|
||||||
|
@ -132,27 +130,22 @@ impl ApubObject for ApubComment {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a `Note` to `Comment`.
|
async fn verify(
|
||||||
///
|
|
||||||
/// If the parent community, post and comment(s) are not known locally, these are also fetched.
|
|
||||||
async fn from_apub(
|
|
||||||
note: &Note,
|
note: &Note,
|
||||||
context: &LemmyContext,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<ApubComment, LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let ap_id = Some(note.id(expected_domain)?.clone().into());
|
verify_domains_match(note.id.inner(), expected_domain)?;
|
||||||
let creator = note
|
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
|
||||||
.attributed_to
|
verify_is_public(¬e.to, ¬e.cc)?;
|
||||||
.dereference(context, request_counter)
|
let (post, _) = note.get_parents(context, request_counter).await?;
|
||||||
.await?;
|
|
||||||
let (post, parent_comment_id) = note.get_parents(context, request_counter).await?;
|
|
||||||
let community_id = post.community_id;
|
let community_id = post.community_id;
|
||||||
let community = blocking(context.pool(), move |conn| {
|
let community = blocking(context.pool(), move |conn| {
|
||||||
Community::read(conn, community_id)
|
Community::read(conn, community_id)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
check_is_apub_id_valid(¬e.id, community.local, &context.settings())?;
|
check_is_apub_id_valid(note.id.inner(), community.local, &context.settings())?;
|
||||||
verify_person_in_community(
|
verify_person_in_community(
|
||||||
¬e.attributed_to,
|
¬e.attributed_to,
|
||||||
&community.into(),
|
&community.into(),
|
||||||
|
@ -163,6 +156,22 @@ impl ApubObject for ApubComment {
|
||||||
if post.locked {
|
if post.locked {
|
||||||
return Err(anyhow!("Post is locked").into());
|
return Err(anyhow!("Post is locked").into());
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a `Note` to `Comment`.
|
||||||
|
///
|
||||||
|
/// If the parent community, post and comment(s) are not known locally, these are also fetched.
|
||||||
|
async fn from_apub(
|
||||||
|
note: Note,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubComment, LemmyError> {
|
||||||
|
let creator = note
|
||||||
|
.attributed_to
|
||||||
|
.dereference(context, request_counter)
|
||||||
|
.await?;
|
||||||
|
let (post, parent_comment_id) = note.get_parents(context, request_counter).await?;
|
||||||
|
|
||||||
let content = if let SourceCompat::Lemmy(source) = ¬e.source {
|
let content = if let SourceCompat::Lemmy(source) = ¬e.source {
|
||||||
source.content.clone()
|
source.content.clone()
|
||||||
|
@ -178,10 +187,10 @@ impl ApubObject for ApubComment {
|
||||||
content: content_slurs_removed,
|
content: content_slurs_removed,
|
||||||
removed: None,
|
removed: None,
|
||||||
read: None,
|
read: None,
|
||||||
published: note.published.map(|u| u.to_owned().naive_local()),
|
published: note.published.map(|u| u.naive_local()),
|
||||||
updated: note.updated.map(|u| u.to_owned().naive_local()),
|
updated: note.updated.map(|u| u.naive_local()),
|
||||||
deleted: None,
|
deleted: None,
|
||||||
ap_id,
|
ap_id: Some(note.id.into()),
|
||||||
local: Some(false),
|
local: Some(false),
|
||||||
};
|
};
|
||||||
let comment = blocking(context.pool(), move |conn| Comment::upsert(conn, &form)).await??;
|
let comment = blocking(context.pool(), move |conn| Comment::upsert(conn, &form)).await??;
|
||||||
|
@ -208,7 +217,10 @@ pub(crate) mod tests {
|
||||||
let person = parse_lemmy_person(context).await;
|
let person = parse_lemmy_person(context).await;
|
||||||
let community = parse_lemmy_community(context).await;
|
let community = parse_lemmy_community(context).await;
|
||||||
let post_json = file_to_json_object("assets/lemmy/objects/page.json");
|
let post_json = file_to_json_object("assets/lemmy/objects/page.json");
|
||||||
let post = ApubPost::from_apub(&post_json, context, url, &mut 0)
|
ApubPost::verify(&post_json, url, context, &mut 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let post = ApubPost::from_apub(post_json, context, &mut 0)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(person, community, post)
|
(person, community, post)
|
||||||
|
@ -227,21 +239,25 @@ pub(crate) mod tests {
|
||||||
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap();
|
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap();
|
||||||
let data = prepare_comment_test(&url, &context).await;
|
let data = prepare_comment_test(&url, &context).await;
|
||||||
|
|
||||||
let json = file_to_json_object("assets/lemmy/objects/note.json");
|
let json: Note = file_to_json_object("assets/lemmy/objects/note.json");
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let comment = ApubComment::from_apub(&json, &context, &url, &mut request_counter)
|
ApubComment::verify(&json, &url, &context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let comment = ApubComment::from_apub(json.clone(), &context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(comment.ap_id.clone().into_inner(), url);
|
assert_eq!(comment.ap_id, url.into());
|
||||||
assert_eq!(comment.content.len(), 14);
|
assert_eq!(comment.content.len(), 14);
|
||||||
assert!(!comment.local);
|
assert!(!comment.local);
|
||||||
assert_eq!(request_counter, 0);
|
assert_eq!(request_counter, 0);
|
||||||
|
|
||||||
let to_apub = comment.to_apub(&context).await.unwrap();
|
let comment_id = comment.id;
|
||||||
|
let to_apub = comment.into_apub(&context).await.unwrap();
|
||||||
assert_json_include!(actual: json, expected: to_apub);
|
assert_json_include!(actual: json, expected: to_apub);
|
||||||
|
|
||||||
Comment::delete(&*context.pool().get().unwrap(), comment.id).unwrap();
|
Comment::delete(&*context.pool().get().unwrap(), comment_id).unwrap();
|
||||||
cleanup(data, &context);
|
cleanup(data, &context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,16 +272,22 @@ pub(crate) mod tests {
|
||||||
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")
|
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let person_json = file_to_json_object("assets/pleroma/objects/person.json");
|
let person_json = file_to_json_object("assets/pleroma/objects/person.json");
|
||||||
ApubPerson::from_apub(&person_json, &context, &pleroma_url, &mut 0)
|
ApubPerson::verify(&person_json, &pleroma_url, &context, &mut 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
ApubPerson::from_apub(person_json, &context, &mut 0)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let json = file_to_json_object("assets/pleroma/objects/note.json");
|
let json = file_to_json_object("assets/pleroma/objects/note.json");
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let comment = ApubComment::from_apub(&json, &context, &pleroma_url, &mut request_counter)
|
ApubComment::verify(&json, &pleroma_url, &context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let comment = ApubComment::from_apub(json, &context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(comment.ap_id.clone().into_inner(), pleroma_url);
|
assert_eq!(comment.ap_id, pleroma_url.into());
|
||||||
assert_eq!(comment.content.len(), 64);
|
assert_eq!(comment.content.len(), 64);
|
||||||
assert!(!comment.local);
|
assert!(!comment.local);
|
||||||
assert_eq!(request_counter, 0);
|
assert_eq!(request_counter, 0);
|
||||||
|
|
|
@ -1,27 +1,20 @@
|
||||||
use activitystreams::{
|
|
||||||
actor::{kind::GroupType, Endpoints},
|
|
||||||
object::kind::ImageType,
|
|
||||||
};
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
use itertools::Itertools;
|
|
||||||
use log::debug;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
collections::{community_moderators::ApubCommunityModerators, CommunityContext},
|
collections::{community_moderators::ApubCommunityModerators, CommunityContext},
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
generate_outbox_url,
|
generate_outbox_url,
|
||||||
protocol::{
|
protocol::{
|
||||||
objects::{group::Group, tombstone::Tombstone},
|
objects::{group::Group, tombstone::Tombstone, Endpoints},
|
||||||
ImageObject,
|
ImageObject,
|
||||||
Source,
|
Source,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use activitystreams::{actor::kind::GroupType, object::kind::ImageType};
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use itertools::Itertools;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActorType, ApubObject},
|
traits::{ActorType, ApubObject},
|
||||||
values::MediaTypeMarkdown,
|
values::MediaTypeMarkdown,
|
||||||
};
|
};
|
||||||
|
@ -32,6 +25,9 @@ use lemmy_utils::{
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use log::debug;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ApubCommunity(Community);
|
pub struct ApubCommunity(Community);
|
||||||
|
@ -80,7 +76,7 @@ impl ApubObject for ApubCommunity {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_apub(&self, _context: &LemmyContext) -> Result<Group, LemmyError> {
|
async fn into_apub(self, _context: &LemmyContext) -> Result<Group, LemmyError> {
|
||||||
let source = self.description.clone().map(|bio| Source {
|
let source = self.description.clone().map(|bio| Source {
|
||||||
content: bio,
|
content: bio,
|
||||||
media_type: MediaTypeMarkdown::Markdown,
|
media_type: MediaTypeMarkdown::Markdown,
|
||||||
|
@ -96,7 +92,7 @@ impl ApubObject for ApubCommunity {
|
||||||
|
|
||||||
let group = Group {
|
let group = Group {
|
||||||
kind: GroupType::Group,
|
kind: GroupType::Group,
|
||||||
id: self.actor_id(),
|
id: ObjectId::new(self.actor_id()),
|
||||||
preferred_username: self.name.clone(),
|
preferred_username: self.name.clone(),
|
||||||
name: self.title.clone(),
|
name: self.title.clone(),
|
||||||
summary: self.description.as_ref().map(|b| markdown_to_html(b)),
|
summary: self.description.as_ref().map(|b| markdown_to_html(b)),
|
||||||
|
@ -105,14 +101,13 @@ impl ApubObject for ApubCommunity {
|
||||||
image,
|
image,
|
||||||
sensitive: Some(self.nsfw),
|
sensitive: Some(self.nsfw),
|
||||||
moderators: Some(ObjectId::<ApubCommunityModerators>::new(
|
moderators: Some(ObjectId::<ApubCommunityModerators>::new(
|
||||||
generate_moderators_url(&self.actor_id)?.into_inner(),
|
generate_moderators_url(&self.actor_id)?,
|
||||||
)),
|
)),
|
||||||
inbox: self.inbox_url.clone().into(),
|
inbox: self.inbox_url.clone().into(),
|
||||||
outbox: ObjectId::new(generate_outbox_url(&self.actor_id)?),
|
outbox: ObjectId::new(generate_outbox_url(&self.actor_id)?),
|
||||||
followers: self.followers_url.clone().into(),
|
followers: self.followers_url.clone().into(),
|
||||||
endpoints: Endpoints {
|
endpoints: Endpoints {
|
||||||
shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
|
shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
|
||||||
..Default::default()
|
|
||||||
},
|
},
|
||||||
public_key: self.get_public_key()?,
|
public_key: self.get_public_key()?,
|
||||||
published: Some(convert_datetime(self.published)),
|
published: Some(convert_datetime(self.published)),
|
||||||
|
@ -129,14 +124,22 @@ impl ApubObject for ApubCommunity {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn verify(
|
||||||
|
group: &Group,
|
||||||
|
expected_domain: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
_request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
group.verify(expected_domain, context).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
|
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
|
||||||
async fn from_apub(
|
async fn from_apub(
|
||||||
group: &Group,
|
group: Group,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: &Url,
|
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<ApubCommunity, LemmyError> {
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
let form = Group::from_apub_to_form(group, expected_domain, &context.settings()).await?;
|
let form = Group::into_form(group.clone());
|
||||||
|
|
||||||
// Fetching mods and outbox is not necessary for Lemmy to work, so ignore errors. Besides,
|
// 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.
|
// we need to ignore these errors so that tests can work entirely offline.
|
||||||
|
@ -166,15 +169,9 @@ impl ApubObject for ApubCommunity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActorType for ApubCommunity {
|
impl ActorType for ApubCommunity {
|
||||||
fn is_local(&self) -> bool {
|
|
||||||
self.local
|
|
||||||
}
|
|
||||||
fn actor_id(&self) -> Url {
|
fn actor_id(&self) -> Url {
|
||||||
self.actor_id.to_owned().into()
|
self.actor_id.to_owned().into()
|
||||||
}
|
}
|
||||||
fn name(&self) -> String {
|
|
||||||
self.name.clone()
|
|
||||||
}
|
|
||||||
fn public_key(&self) -> Option<String> {
|
fn public_key(&self) -> Option<String> {
|
||||||
self.public_key.to_owned()
|
self.public_key.to_owned()
|
||||||
}
|
}
|
||||||
|
@ -187,7 +184,7 @@ impl ActorType for ApubCommunity {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shared_inbox_url(&self) -> Option<Url> {
|
fn shared_inbox_url(&self) -> Option<Url> {
|
||||||
self.shared_inbox_url.clone().map(|s| s.into_inner())
|
self.shared_inbox_url.clone().map(|s| s.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,8 +204,12 @@ impl ApubCommunity {
|
||||||
let follower_inboxes: Vec<Url> = follows
|
let follower_inboxes: Vec<Url> = follows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|f| !f.follower.local)
|
.filter(|f| !f.follower.local)
|
||||||
.map(|f| f.follower.shared_inbox_url.unwrap_or(f.follower.inbox_url))
|
.map(|f| {
|
||||||
.map(|i| i.into_inner())
|
f.follower
|
||||||
|
.shared_inbox_url
|
||||||
|
.unwrap_or(f.follower.inbox_url)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let inboxes = vec![follower_inboxes, additional_inboxes]
|
let inboxes = vec![follower_inboxes, additional_inboxes]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -239,7 +240,10 @@ pub(crate) mod tests {
|
||||||
|
|
||||||
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let community = ApubCommunity::from_apub(&json, context, &url, &mut request_counter)
|
ApubCommunity::verify(&json, &url, context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let community = ApubCommunity::from_apub(json, context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// this makes two requests to the (intentionally) broken outbox/moderators collections
|
// this makes two requests to the (intentionally) broken outbox/moderators collections
|
||||||
|
|
|
@ -3,15 +3,19 @@ use crate::{
|
||||||
generate_outbox_url,
|
generate_outbox_url,
|
||||||
objects::get_summary_from_string_or_source,
|
objects::get_summary_from_string_or_source,
|
||||||
protocol::{
|
protocol::{
|
||||||
objects::person::{Person, UserTypes},
|
objects::{
|
||||||
|
person::{Person, UserTypes},
|
||||||
|
Endpoints,
|
||||||
|
},
|
||||||
ImageObject,
|
ImageObject,
|
||||||
Source,
|
Source,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use activitystreams::{actor::Endpoints, object::kind::ImageType};
|
use activitystreams::object::kind::ImageType;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
|
object_id::ObjectId,
|
||||||
traits::{ActorType, ApubObject},
|
traits::{ActorType, ApubObject},
|
||||||
values::MediaTypeMarkdown,
|
values::MediaTypeMarkdown,
|
||||||
verify::verify_domains_match,
|
verify::verify_domains_match,
|
||||||
|
@ -75,7 +79,7 @@ impl ApubObject for ApubPerson {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_apub(&self, _pool: &LemmyContext) -> Result<Person, LemmyError> {
|
async fn into_apub(self, _pool: &LemmyContext) -> Result<Person, LemmyError> {
|
||||||
let kind = if self.bot_account {
|
let kind = if self.bot_account {
|
||||||
UserTypes::Service
|
UserTypes::Service
|
||||||
} else {
|
} else {
|
||||||
|
@ -96,7 +100,7 @@ impl ApubObject for ApubPerson {
|
||||||
|
|
||||||
let person = Person {
|
let person = Person {
|
||||||
kind,
|
kind,
|
||||||
id: self.actor_id.to_owned().into_inner(),
|
id: ObjectId::new(self.actor_id.clone()),
|
||||||
preferred_username: self.name.clone(),
|
preferred_username: self.name.clone(),
|
||||||
name: self.display_name.clone(),
|
name: self.display_name.clone(),
|
||||||
summary: self.bio.as_ref().map(|b| markdown_to_html(b)),
|
summary: self.bio.as_ref().map(|b| markdown_to_html(b)),
|
||||||
|
@ -108,7 +112,6 @@ impl ApubObject for ApubPerson {
|
||||||
outbox: generate_outbox_url(&self.actor_id)?.into(),
|
outbox: generate_outbox_url(&self.actor_id)?.into(),
|
||||||
endpoints: Endpoints {
|
endpoints: Endpoints {
|
||||||
shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
|
shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
|
||||||
..Default::default()
|
|
||||||
},
|
},
|
||||||
public_key: self.get_public_key()?,
|
public_key: self.get_public_key()?,
|
||||||
updated: self.updated.map(convert_datetime),
|
updated: self.updated.map(convert_datetime),
|
||||||
|
@ -122,50 +125,51 @@ impl ApubObject for ApubPerson {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn from_apub(
|
async fn verify(
|
||||||
person: &Person,
|
person: &Person,
|
||||||
context: &LemmyContext,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
_request_counter: &mut i32,
|
_request_counter: &mut i32,
|
||||||
) -> Result<ApubPerson, LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_domains_match(&person.id, expected_domain)?;
|
verify_domains_match(person.id.inner(), expected_domain)?;
|
||||||
let actor_id = Some(person.id.clone().into());
|
check_is_apub_id_valid(person.id.inner(), false, &context.settings())?;
|
||||||
let name = person.preferred_username.clone();
|
|
||||||
let display_name: Option<String> = person.name.clone();
|
|
||||||
let bio = get_summary_from_string_or_source(&person.summary, &person.source);
|
|
||||||
let shared_inbox = person.endpoints.shared_inbox.clone().map(|s| s.into());
|
|
||||||
let bot_account = match person.kind {
|
|
||||||
UserTypes::Person => false,
|
|
||||||
UserTypes::Service => true,
|
|
||||||
};
|
|
||||||
|
|
||||||
let slur_regex = &context.settings().slur_regex();
|
let slur_regex = &context.settings().slur_regex();
|
||||||
check_slurs(&name, slur_regex)?;
|
check_slurs(&person.preferred_username, slur_regex)?;
|
||||||
check_slurs_opt(&display_name, slur_regex)?;
|
check_slurs_opt(&person.name, slur_regex)?;
|
||||||
|
let bio = get_summary_from_string_or_source(&person.summary, &person.source);
|
||||||
check_slurs_opt(&bio, slur_regex)?;
|
check_slurs_opt(&bio, slur_regex)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
check_is_apub_id_valid(&person.id, false, &context.settings())?;
|
async fn from_apub(
|
||||||
|
person: Person,
|
||||||
|
context: &LemmyContext,
|
||||||
|
_request_counter: &mut i32,
|
||||||
|
) -> Result<ApubPerson, LemmyError> {
|
||||||
let person_form = PersonForm {
|
let person_form = PersonForm {
|
||||||
name,
|
name: person.preferred_username,
|
||||||
display_name: Some(display_name),
|
display_name: Some(person.name),
|
||||||
banned: None,
|
banned: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
avatar: Some(person.icon.clone().map(|i| i.url.into())),
|
avatar: Some(person.icon.map(|i| i.url.into())),
|
||||||
banner: Some(person.image.clone().map(|i| i.url.into())),
|
banner: Some(person.image.map(|i| i.url.into())),
|
||||||
published: person.published.map(|u| u.clone().naive_local()),
|
published: person.published.map(|u| u.naive_local()),
|
||||||
updated: person.updated.map(|u| u.clone().naive_local()),
|
updated: person.updated.map(|u| u.naive_local()),
|
||||||
actor_id,
|
actor_id: Some(person.id.into()),
|
||||||
bio: Some(bio),
|
bio: Some(get_summary_from_string_or_source(
|
||||||
|
&person.summary,
|
||||||
|
&person.source,
|
||||||
|
)),
|
||||||
local: Some(false),
|
local: Some(false),
|
||||||
admin: Some(false),
|
admin: Some(false),
|
||||||
bot_account: Some(bot_account),
|
bot_account: Some(person.kind == UserTypes::Service),
|
||||||
private_key: None,
|
private_key: None,
|
||||||
public_key: Some(Some(person.public_key.public_key_pem.clone())),
|
public_key: Some(Some(person.public_key.public_key_pem)),
|
||||||
last_refreshed_at: Some(naive_now()),
|
last_refreshed_at: Some(naive_now()),
|
||||||
inbox_url: Some(person.inbox.to_owned().into()),
|
inbox_url: Some(person.inbox.into()),
|
||||||
shared_inbox_url: Some(shared_inbox),
|
shared_inbox_url: Some(person.endpoints.shared_inbox.map(|s| s.into())),
|
||||||
matrix_user_id: Some(person.matrix_user_id.clone()),
|
matrix_user_id: Some(person.matrix_user_id),
|
||||||
};
|
};
|
||||||
let person = blocking(context.pool(), move |conn| {
|
let person = blocking(context.pool(), move |conn| {
|
||||||
DbPerson::upsert(conn, &person_form)
|
DbPerson::upsert(conn, &person_form)
|
||||||
|
@ -176,14 +180,8 @@ impl ApubObject for ApubPerson {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActorType for ApubPerson {
|
impl ActorType for ApubPerson {
|
||||||
fn is_local(&self) -> bool {
|
|
||||||
self.local
|
|
||||||
}
|
|
||||||
fn actor_id(&self) -> Url {
|
fn actor_id(&self) -> Url {
|
||||||
self.actor_id.to_owned().into_inner()
|
self.actor_id.to_owned().into()
|
||||||
}
|
|
||||||
fn name(&self) -> String {
|
|
||||||
self.name.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn public_key(&self) -> Option<String> {
|
fn public_key(&self) -> Option<String> {
|
||||||
|
@ -199,7 +197,7 @@ impl ActorType for ApubPerson {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shared_inbox_url(&self) -> Option<Url> {
|
fn shared_inbox_url(&self) -> Option<Url> {
|
||||||
self.shared_inbox_url.clone().map(|s| s.into_inner())
|
self.shared_inbox_url.clone().map(|s| s.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +212,10 @@ pub(crate) mod tests {
|
||||||
let json = file_to_json_object("assets/lemmy/objects/person.json");
|
let json = file_to_json_object("assets/lemmy/objects/person.json");
|
||||||
let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap();
|
let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap();
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let person = ApubPerson::from_apub(&json, context, &url, &mut request_counter)
|
ApubPerson::verify(&json, &url, context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let person = ApubPerson::from_apub(json, context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(request_counter, 0);
|
assert_eq!(request_counter, 0);
|
||||||
|
@ -242,11 +243,14 @@ pub(crate) mod tests {
|
||||||
let json = file_to_json_object("assets/pleroma/objects/person.json");
|
let json = file_to_json_object("assets/pleroma/objects/person.json");
|
||||||
let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let person = ApubPerson::from_apub(&json, &context, &url, &mut request_counter)
|
ApubPerson::verify(&json, &url, &context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let person = ApubPerson::from_apub(json, &context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(person.actor_id.clone().into_inner(), url);
|
assert_eq!(person.actor_id, url.into());
|
||||||
assert_eq!(person.name, "lanodan");
|
assert_eq!(person.name, "lanodan");
|
||||||
assert!(person.public_key.is_some());
|
assert!(person.public_key.is_some());
|
||||||
assert!(!person.local);
|
assert!(!person.local);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::verify_person_in_community,
|
activities::{verify_is_public, verify_person_in_community},
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
protocol::{
|
protocol::{
|
||||||
objects::{page::Page, tombstone::Tombstone},
|
objects::{page::Page, tombstone::Tombstone},
|
||||||
ImageObject,
|
ImageObject,
|
||||||
|
@ -15,8 +14,10 @@ use activitystreams::{
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
|
object_id::ObjectId,
|
||||||
traits::ApubObject,
|
traits::ApubObject,
|
||||||
values::{MediaTypeHtml, MediaTypeMarkdown},
|
values::{MediaTypeHtml, MediaTypeMarkdown},
|
||||||
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
self,
|
self,
|
||||||
|
@ -29,7 +30,7 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
request::fetch_site_data,
|
request::fetch_site_data,
|
||||||
utils::{convert_datetime, markdown_to_html, remove_slurs},
|
utils::{check_slurs, convert_datetime, markdown_to_html, remove_slurs},
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
@ -86,7 +87,7 @@ impl ApubObject for ApubPost {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
|
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
|
||||||
async fn to_apub(&self, context: &LemmyContext) -> Result<Page, LemmyError> {
|
async fn into_apub(self, context: &LemmyContext) -> Result<Page, LemmyError> {
|
||||||
let creator_id = self.creator_id;
|
let creator_id = self.creator_id;
|
||||||
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
|
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
|
||||||
let community_id = self.community_id;
|
let community_id = self.community_id;
|
||||||
|
@ -106,9 +107,10 @@ impl ApubObject for ApubPost {
|
||||||
|
|
||||||
let page = Page {
|
let page = Page {
|
||||||
r#type: PageType::Page,
|
r#type: PageType::Page,
|
||||||
id: self.ap_id.clone().into(),
|
id: ObjectId::new(self.ap_id.clone()),
|
||||||
attributed_to: ObjectId::new(creator.actor_id),
|
attributed_to: ObjectId::new(creator.actor_id),
|
||||||
to: vec![community.actor_id.into(), public()],
|
to: vec![community.actor_id.into(), public()],
|
||||||
|
cc: vec![],
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
content: self.body.as_ref().map(|b| markdown_to_html(b)),
|
content: self.body.as_ref().map(|b| markdown_to_html(b)),
|
||||||
media_type: Some(MediaTypeHtml::Html),
|
media_type: Some(MediaTypeHtml::Html),
|
||||||
|
@ -132,29 +134,39 @@ impl ApubObject for ApubPost {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn from_apub(
|
async fn verify(
|
||||||
page: &Page,
|
page: &Page,
|
||||||
context: &LemmyContext,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<ApubPost, LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
// We can't verify the domain in case of mod action, because the mod may be on a different
|
// We can't verify the domain in case of mod action, because the mod may be on a different
|
||||||
// instance from the post author.
|
// instance from the post author.
|
||||||
let ap_id = if page.is_mod_action(context).await? {
|
if !page.is_mod_action(context).await? {
|
||||||
page.id_unchecked()
|
verify_domains_match(page.id.inner(), expected_domain)?;
|
||||||
} else {
|
|
||||||
page.id(expected_domain)?
|
|
||||||
};
|
};
|
||||||
let ap_id = Some(ap_id.clone().into());
|
|
||||||
|
let community = page.extract_community(context, request_counter).await?;
|
||||||
|
check_is_apub_id_valid(page.id.inner(), community.local, &context.settings())?;
|
||||||
|
verify_person_in_community(&page.attributed_to, &community, context, request_counter).await?;
|
||||||
|
check_slurs(&page.name, &context.settings().slur_regex())?;
|
||||||
|
verify_domains_match(page.attributed_to.inner(), page.id.inner())?;
|
||||||
|
verify_is_public(&page.to, &page.cc)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_apub(
|
||||||
|
page: Page,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubPost, LemmyError> {
|
||||||
let creator = page
|
let creator = page
|
||||||
.attributed_to
|
.attributed_to
|
||||||
.dereference(context, request_counter)
|
.dereference(context, request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
let community = page.extract_community(context, request_counter).await?;
|
let community = page.extract_community(context, request_counter).await?;
|
||||||
check_is_apub_id_valid(&page.id, community.local, &context.settings())?;
|
|
||||||
verify_person_in_community(&page.attributed_to, &community, context, request_counter).await?;
|
|
||||||
|
|
||||||
let thumbnail_url: Option<Url> = page.image.clone().map(|i| i.url);
|
let thumbnail_url: Option<Url> = page.image.map(|i| i.url);
|
||||||
let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url {
|
let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url {
|
||||||
fetch_site_data(context.client(), &context.settings(), Some(url)).await
|
fetch_site_data(context.client(), &context.settings(), Some(url)).await
|
||||||
} else {
|
} else {
|
||||||
|
@ -169,8 +181,8 @@ impl ApubObject for ApubPost {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|s| remove_slurs(&s.content, &context.settings().slur_regex()));
|
.map(|s| remove_slurs(&s.content, &context.settings().slur_regex()));
|
||||||
let form = PostForm {
|
let form = PostForm {
|
||||||
name: page.name.clone(),
|
name: page.name,
|
||||||
url: page.url.clone().map(|u| u.into()),
|
url: page.url.map(|u| u.into()),
|
||||||
body: body_slurs_removed,
|
body: body_slurs_removed,
|
||||||
creator_id: creator.id,
|
creator_id: creator.id,
|
||||||
community_id: community.id,
|
community_id: community.id,
|
||||||
|
@ -185,7 +197,7 @@ impl ApubObject for ApubPost {
|
||||||
embed_description,
|
embed_description,
|
||||||
embed_html,
|
embed_html,
|
||||||
thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
|
thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
|
||||||
ap_id,
|
ap_id: Some(page.id.into()),
|
||||||
local: Some(false),
|
local: Some(false),
|
||||||
};
|
};
|
||||||
let post = blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??;
|
let post = blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??;
|
||||||
|
@ -214,11 +226,14 @@ mod tests {
|
||||||
let json = file_to_json_object("assets/lemmy/objects/page.json");
|
let json = file_to_json_object("assets/lemmy/objects/page.json");
|
||||||
let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap();
|
let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap();
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let post = ApubPost::from_apub(&json, &context, &url, &mut request_counter)
|
ApubPost::verify(&json, &url, &context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let post = ApubPost::from_apub(json, &context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(post.ap_id.clone().into_inner(), url);
|
assert_eq!(post.ap_id, url.into());
|
||||||
assert_eq!(post.name, "Post title");
|
assert_eq!(post.name, "Post title");
|
||||||
assert!(post.body.is_some());
|
assert!(post.body.is_some());
|
||||||
assert_eq!(post.body.as_ref().unwrap().len(), 45);
|
assert_eq!(post.body.as_ref().unwrap().len(), 45);
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use crate::{
|
use crate::protocol::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
protocol::{
|
|
||||||
objects::chat_message::{ChatMessage, ChatMessageType},
|
objects::chat_message::{ChatMessage, ChatMessageType},
|
||||||
Source,
|
Source,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
use anyhow::anyhow;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use html2md::parse_html;
|
use html2md::parse_html;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
|
object_id::ObjectId,
|
||||||
traits::ApubObject,
|
traits::ApubObject,
|
||||||
values::{MediaTypeHtml, MediaTypeMarkdown},
|
values::{MediaTypeHtml, MediaTypeMarkdown},
|
||||||
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -71,7 +71,7 @@ impl ApubObject for ApubPrivateMessage {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_apub(&self, context: &LemmyContext) -> Result<ChatMessage, LemmyError> {
|
async fn into_apub(self, context: &LemmyContext) -> Result<ChatMessage, LemmyError> {
|
||||||
let creator_id = self.creator_id;
|
let creator_id = self.creator_id;
|
||||||
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
|
let creator = blocking(context.pool(), move |conn| Person::read(conn, creator_id)).await??;
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ impl ApubObject for ApubPrivateMessage {
|
||||||
|
|
||||||
let note = ChatMessage {
|
let note = ChatMessage {
|
||||||
r#type: ChatMessageType::ChatMessage,
|
r#type: ChatMessageType::ChatMessage,
|
||||||
id: self.ap_id.clone().into(),
|
id: ObjectId::new(self.ap_id.clone()),
|
||||||
attributed_to: ObjectId::new(creator.actor_id),
|
attributed_to: ObjectId::new(creator.actor_id),
|
||||||
to: [ObjectId::new(recipient.actor_id)],
|
to: [ObjectId::new(recipient.actor_id)],
|
||||||
content: markdown_to_html(&self.content),
|
content: markdown_to_html(&self.content),
|
||||||
|
@ -101,13 +101,29 @@ impl ApubObject for ApubPrivateMessage {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn from_apub(
|
async fn verify(
|
||||||
note: &ChatMessage,
|
note: &ChatMessage,
|
||||||
context: &LemmyContext,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_domains_match(note.id.inner(), expected_domain)?;
|
||||||
|
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
|
||||||
|
let person = note
|
||||||
|
.attributed_to
|
||||||
|
.dereference(context, request_counter)
|
||||||
|
.await?;
|
||||||
|
if person.banned {
|
||||||
|
return Err(anyhow!("Person is banned from site").into());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_apub(
|
||||||
|
note: ChatMessage,
|
||||||
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<ApubPrivateMessage, LemmyError> {
|
) -> Result<ApubPrivateMessage, LemmyError> {
|
||||||
let ap_id = Some(note.id(expected_domain)?.clone().into());
|
|
||||||
let creator = note
|
let creator = note
|
||||||
.attributed_to
|
.attributed_to
|
||||||
.dereference(context, request_counter)
|
.dereference(context, request_counter)
|
||||||
|
@ -123,11 +139,11 @@ impl ApubObject for ApubPrivateMessage {
|
||||||
creator_id: creator.id,
|
creator_id: creator.id,
|
||||||
recipient_id: recipient.id,
|
recipient_id: recipient.id,
|
||||||
content,
|
content,
|
||||||
published: note.published.map(|u| u.to_owned().naive_local()),
|
published: note.published.map(|u| u.naive_local()),
|
||||||
updated: note.updated.map(|u| u.to_owned().naive_local()),
|
updated: note.updated.map(|u| u.naive_local()),
|
||||||
deleted: None,
|
deleted: None,
|
||||||
read: None,
|
read: None,
|
||||||
ap_id,
|
ap_id: Some(note.id.into()),
|
||||||
local: Some(false),
|
local: Some(false),
|
||||||
};
|
};
|
||||||
let pm = blocking(context.pool(), move |conn| {
|
let pm = blocking(context.pool(), move |conn| {
|
||||||
|
@ -150,12 +166,18 @@ mod tests {
|
||||||
|
|
||||||
async fn prepare_comment_test(url: &Url, context: &LemmyContext) -> (ApubPerson, ApubPerson) {
|
async fn prepare_comment_test(url: &Url, context: &LemmyContext) -> (ApubPerson, ApubPerson) {
|
||||||
let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json");
|
let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json");
|
||||||
let person1 = ApubPerson::from_apub(&lemmy_person, context, url, &mut 0)
|
ApubPerson::verify(&lemmy_person, url, context, &mut 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let person1 = ApubPerson::from_apub(lemmy_person, context, &mut 0)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json");
|
let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json");
|
||||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
||||||
let person2 = ApubPerson::from_apub(&pleroma_person, context, &pleroma_url, &mut 0)
|
ApubPerson::verify(&pleroma_person, &pleroma_url, context, &mut 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let person2 = ApubPerson::from_apub(pleroma_person, context, &mut 0)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(person1, person2)
|
(person1, person2)
|
||||||
|
@ -172,20 +194,24 @@ mod tests {
|
||||||
let context = init_context();
|
let context = init_context();
|
||||||
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap();
|
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap();
|
||||||
let data = prepare_comment_test(&url, &context).await;
|
let data = prepare_comment_test(&url, &context).await;
|
||||||
let json = file_to_json_object("assets/lemmy/objects/chat_message.json");
|
let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json");
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let pm = ApubPrivateMessage::from_apub(&json, &context, &url, &mut request_counter)
|
ApubPrivateMessage::verify(&json, &url, &context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let pm = ApubPrivateMessage::from_apub(json.clone(), &context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(pm.ap_id.clone().into_inner(), url);
|
assert_eq!(pm.ap_id.clone(), url.into());
|
||||||
assert_eq!(pm.content.len(), 20);
|
assert_eq!(pm.content.len(), 20);
|
||||||
assert_eq!(request_counter, 0);
|
assert_eq!(request_counter, 0);
|
||||||
|
|
||||||
let to_apub = pm.to_apub(&context).await.unwrap();
|
let pm_id = pm.id;
|
||||||
|
let to_apub = pm.into_apub(&context).await.unwrap();
|
||||||
assert_json_include!(actual: json, expected: to_apub);
|
assert_json_include!(actual: json, expected: to_apub);
|
||||||
|
|
||||||
PrivateMessage::delete(&*context.pool().get().unwrap(), pm.id).unwrap();
|
PrivateMessage::delete(&*context.pool().get().unwrap(), pm_id).unwrap();
|
||||||
cleanup(data, &context);
|
cleanup(data, &context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,11 +224,14 @@ mod tests {
|
||||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2").unwrap();
|
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2").unwrap();
|
||||||
let json = file_to_json_object("assets/pleroma/objects/chat_message.json");
|
let json = file_to_json_object("assets/pleroma/objects/chat_message.json");
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let pm = ApubPrivateMessage::from_apub(&json, &context, &pleroma_url, &mut request_counter)
|
ApubPrivateMessage::verify(&json, &pleroma_url, &context, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let pm = ApubPrivateMessage::from_apub(json, &context, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(pm.ap_id.clone().into_inner(), pleroma_url);
|
assert_eq!(pm.ap_id, pleroma_url.into());
|
||||||
assert_eq!(pm.content.len(), 3);
|
assert_eq!(pm.content.len(), 3);
|
||||||
assert_eq!(request_counter, 0);
|
assert_eq!(request_counter, 0);
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson};
|
use crate::objects::person::ApubPerson;
|
||||||
use activitystreams::{activity::kind::AddType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::AddType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AddMod {
|
pub struct AddMod {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
use crate::{
|
use crate::{activity_lists::AnnouncableActivities, objects::community::ApubCommunity};
|
||||||
activity_lists::AnnouncableActivities,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::community::ApubCommunity,
|
|
||||||
};
|
|
||||||
use activitystreams::{activity::kind::AnnounceType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::AnnounceType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AnnounceActivity {
|
pub struct AnnounceActivity {
|
||||||
pub(crate) actor: ObjectId<ApubCommunity>,
|
pub(crate) actor: ObjectId<ApubCommunity>,
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use crate::{
|
use crate::objects::{community::ApubCommunity, person::ApubPerson};
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
};
|
|
||||||
use activitystreams::{activity::kind::BlockType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::BlockType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BlockUserFromCommunity {
|
pub struct BlockUserFromCommunity {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson};
|
use crate::objects::person::ApubPerson;
|
||||||
use activitystreams::{activity::kind::RemoveType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::RemoveType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RemoveMod {
|
pub struct RemoveMod {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::{object_id::ObjectId, post_or_comment::PostOrComment},
|
fetcher::post_or_comment::PostOrComment,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::FlagType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::FlagType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Report {
|
pub struct Report {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::activities::community::block_user::BlockUserFromCommunity,
|
protocol::activities::community::block_user::BlockUserFromCommunity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoBlockUserFromCommunity {
|
pub struct UndoBlockUserFromCommunity {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
use crate::{
|
use crate::{objects::person::ApubPerson, protocol::objects::group::Group};
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::person::ApubPerson,
|
|
||||||
protocol::objects::group::Group,
|
|
||||||
};
|
|
||||||
use activitystreams::{activity::kind::UpdateType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::UpdateType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
/// This activity is received from a remote community mod, and updates the description or other
|
/// This activity is received from a remote community mod, and updates the description or other
|
||||||
/// fields of a local community.
|
/// fields of a local community.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UpdateCommunity {
|
pub struct UpdateCommunity {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
pub(crate) to: Vec<Url>,
|
pub(crate) to: Vec<Url>,
|
||||||
// TODO: would be nice to use a separate struct here, which only contains the fields updated here
|
// TODO: would be nice to use a separate struct here, which only contains the fields updated here
|
||||||
pub(crate) object: Group,
|
pub(crate) object: Box<Group>,
|
||||||
pub(crate) cc: Vec<Url>,
|
pub(crate) cc: Vec<Url>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub(crate) kind: UpdateType,
|
pub(crate) kind: UpdateType,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::{activities::CreateOrUpdateType, objects::note::Note},
|
protocol::{activities::CreateOrUpdateType, objects::note::Note},
|
||||||
};
|
};
|
||||||
use activitystreams::{link::Mention, unparsed::Unparsed};
|
use activitystreams::{link::Mention, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateOrUpdateComment {
|
pub struct CreateOrUpdateComment {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::{activities::CreateOrUpdateType, objects::page::Page},
|
protocol::{activities::CreateOrUpdateType, objects::page::Page},
|
||||||
};
|
};
|
||||||
use activitystreams::unparsed::Unparsed;
|
use activitystreams::unparsed::Unparsed;
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateOrUpdatePost {
|
pub struct CreateOrUpdatePost {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson};
|
use crate::objects::person::ApubPerson;
|
||||||
use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Delete {
|
pub struct Delete {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
|
use crate::{objects::person::ApubPerson, protocol::activities::deletion::delete::Delete};
|
||||||
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
||||||
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
|
||||||
use crate::{
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::person::ApubPerson,
|
|
||||||
protocol::activities::deletion::delete::Delete,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDelete {
|
pub struct UndoDelete {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::following::follow::FollowCommunity,
|
protocol::activities::following::follow::FollowCommunity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::AcceptType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::AcceptType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AcceptFollowCommunity {
|
pub struct AcceptFollowCommunity {
|
||||||
pub(crate) actor: ObjectId<ApubCommunity>,
|
pub(crate) actor: ObjectId<ApubCommunity>,
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use crate::{
|
use crate::objects::{community::ApubCommunity, person::ApubPerson};
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
};
|
|
||||||
use activitystreams::{activity::kind::FollowType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::FollowType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct FollowCommunity {
|
pub struct FollowCommunity {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::following::follow::FollowCommunity,
|
protocol::activities::following::follow::FollowCommunity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoFollowCommunity {
|
pub struct UndoFollowCommunity {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage},
|
protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage},
|
||||||
};
|
};
|
||||||
use activitystreams::unparsed::Unparsed;
|
use activitystreams::unparsed::Unparsed;
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateOrUpdatePrivateMessage {
|
pub struct CreateOrUpdatePrivateMessage {
|
||||||
pub(crate) id: Url,
|
pub(crate) id: Url,
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use crate::{
|
use crate::objects::{person::ApubPerson, private_message::ApubPrivateMessage};
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
|
||||||
};
|
|
||||||
use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::DeleteType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DeletePrivateMessage {
|
pub struct DeletePrivateMessage {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::activities::private_message::delete::DeletePrivateMessage,
|
protocol::activities::private_message::delete::DeletePrivateMessage,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDeletePrivateMessage {
|
pub struct UndoDeletePrivateMessage {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
|
use crate::{objects::person::ApubPerson, protocol::activities::voting::vote::Vote};
|
||||||
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
use activitystreams::{activity::kind::UndoType, unparsed::Unparsed};
|
||||||
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
|
||||||
use crate::{
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::person::ApubPerson,
|
|
||||||
protocol::activities::voting::vote::Vote,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoVote {
|
pub struct UndoVote {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
use crate::{
|
use crate::{fetcher::post_or_comment::PostOrComment, objects::person::ApubPerson};
|
||||||
fetcher::{object_id::ObjectId, post_or_comment::PostOrComment},
|
|
||||||
objects::person::ApubPerson,
|
|
||||||
};
|
|
||||||
use activitystreams::unparsed::Unparsed;
|
use activitystreams::unparsed::Unparsed;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_apub_lib::traits::ActivityFields;
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use strum_macros::ToString;
|
use strum_macros::ToString;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Vote {
|
pub struct Vote {
|
||||||
pub(crate) actor: ObjectId<ApubPerson>,
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl GroupFollowers {
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
Ok(GroupFollowers {
|
Ok(GroupFollowers {
|
||||||
id: generate_followers_url(&community.actor_id)?.into_inner(),
|
id: generate_followers_url(&community.actor_id)?.into(),
|
||||||
r#type: CollectionType::Collection,
|
r#type: CollectionType::Collection,
|
||||||
total_items: community_followers.len() as i32,
|
total_items: community_followers.len() as i32,
|
||||||
items: vec![],
|
items: vec![],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson};
|
use crate::objects::person::ApubPerson;
|
||||||
use activitystreams::collection::kind::OrderedCollectionType;
|
use activitystreams::collection::kind::OrderedCollectionType;
|
||||||
|
use lemmy_apub_lib::object_id::ObjectId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ impl PersonOutbox {
|
||||||
pub(crate) async fn new(user: Person) -> Result<PersonOutbox, LemmyError> {
|
pub(crate) async fn new(user: Person) -> Result<PersonOutbox, LemmyError> {
|
||||||
Ok(PersonOutbox {
|
Ok(PersonOutbox {
|
||||||
r#type: OrderedCollectionType::OrderedCollection,
|
r#type: OrderedCollectionType::OrderedCollection,
|
||||||
id: generate_outbox_url(&user.actor_id)?.into_inner(),
|
id: generate_outbox_url(&user.actor_id)?.into(),
|
||||||
ordered_items: vec![],
|
ordered_items: vec![],
|
||||||
total_items: 0,
|
total_items: 0,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson, protocol::Source};
|
use crate::{
|
||||||
|
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||||
|
protocol::Source,
|
||||||
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
chrono::{DateTime, FixedOffset},
|
chrono::{DateTime, FixedOffset},
|
||||||
unparsed::Unparsed,
|
unparsed::Unparsed,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml};
|
||||||
use lemmy_apub_lib::{values::MediaTypeHtml, verify::verify_domains_match};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ChatMessage {
|
pub struct ChatMessage {
|
||||||
pub(crate) r#type: ChatMessageType,
|
pub(crate) r#type: ChatMessageType,
|
||||||
pub(crate) id: Url,
|
pub(crate) id: ObjectId<ApubPrivateMessage>,
|
||||||
pub(crate) attributed_to: ObjectId<ApubPerson>,
|
pub(crate) attributed_to: ObjectId<ApubPerson>,
|
||||||
pub(crate) to: [ObjectId<ApubPerson>; 1],
|
pub(crate) to: [ObjectId<ApubPerson>; 1],
|
||||||
pub(crate) content: String,
|
pub(crate) content: String,
|
||||||
|
@ -33,29 +32,3 @@ pub struct ChatMessage {
|
||||||
pub enum ChatMessageType {
|
pub enum ChatMessageType {
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChatMessage {
|
|
||||||
pub(crate) fn id_unchecked(&self) -> &Url {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
|
|
||||||
verify_domains_match(&self.id, expected_domain)?;
|
|
||||||
Ok(&self.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn verify(
|
|
||||||
&self,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
verify_domains_match(self.attributed_to.inner(), &self.id)?;
|
|
||||||
let person = self
|
|
||||||
.attributed_to
|
|
||||||
.dereference(context, request_counter)
|
|
||||||
.await?;
|
|
||||||
if person.banned {
|
|
||||||
return Err(anyhow!("Person is banned from site").into());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,22 +4,18 @@ use crate::{
|
||||||
community_moderators::ApubCommunityModerators,
|
community_moderators::ApubCommunityModerators,
|
||||||
community_outbox::ApubCommunityOutbox,
|
community_outbox::ApubCommunityOutbox,
|
||||||
},
|
},
|
||||||
fetcher::object_id::ObjectId,
|
objects::{community::ApubCommunity, get_summary_from_string_or_source},
|
||||||
objects::get_summary_from_string_or_source,
|
protocol::{objects::Endpoints, ImageObject, Source},
|
||||||
protocol::{ImageObject, Source},
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
actor::{kind::GroupType, Endpoints},
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
|
use activitystreams::{actor::kind::GroupType, unparsed::Unparsed};
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use lemmy_apub_lib::{signatures::PublicKey, verify::verify_domains_match};
|
use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey, verify::verify_domains_match};
|
||||||
use lemmy_db_schema::{naive_now, source::community::CommunityForm};
|
use lemmy_db_schema::{naive_now, source::community::CommunityForm};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
settings::structs::Settings,
|
|
||||||
utils::{check_slurs, check_slurs_opt},
|
utils::{check_slurs, check_slurs_opt},
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -30,7 +26,7 @@ use url::Url;
|
||||||
pub struct Group {
|
pub struct Group {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub(crate) kind: GroupType,
|
pub(crate) kind: GroupType,
|
||||||
pub(crate) id: Url,
|
pub(crate) id: ObjectId<ApubCommunity>,
|
||||||
/// username, set at account creation and can never be changed
|
/// username, set at account creation and can never be changed
|
||||||
pub(crate) preferred_username: String,
|
pub(crate) preferred_username: String,
|
||||||
/// title (can be changed at any time)
|
/// title (can be changed at any time)
|
||||||
|
@ -47,7 +43,7 @@ pub struct Group {
|
||||||
pub(crate) inbox: Url,
|
pub(crate) inbox: Url,
|
||||||
pub(crate) outbox: ObjectId<ApubCommunityOutbox>,
|
pub(crate) outbox: ObjectId<ApubCommunityOutbox>,
|
||||||
pub(crate) followers: Url,
|
pub(crate) followers: Url,
|
||||||
pub(crate) endpoints: Endpoints<Url>,
|
pub(crate) endpoints: Endpoints,
|
||||||
pub(crate) public_key: PublicKey,
|
pub(crate) public_key: PublicKey,
|
||||||
pub(crate) published: Option<DateTime<FixedOffset>>,
|
pub(crate) published: Option<DateTime<FixedOffset>>,
|
||||||
pub(crate) updated: Option<DateTime<FixedOffset>>,
|
pub(crate) updated: Option<DateTime<FixedOffset>>,
|
||||||
|
@ -56,42 +52,42 @@ pub struct Group {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Group {
|
impl Group {
|
||||||
pub(crate) async fn from_apub_to_form(
|
pub(crate) async fn verify(
|
||||||
group: &Group,
|
&self,
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
settings: &Settings,
|
context: &LemmyContext,
|
||||||
) -> Result<CommunityForm, LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
check_is_apub_id_valid(&group.id, true, settings)?;
|
check_is_apub_id_valid(self.id.inner(), true, &context.settings())?;
|
||||||
verify_domains_match(expected_domain, &group.id)?;
|
verify_domains_match(expected_domain, self.id.inner())?;
|
||||||
let name = group.preferred_username.clone();
|
|
||||||
let title = group.name.clone();
|
|
||||||
let description = get_summary_from_string_or_source(&group.summary, &group.source);
|
|
||||||
let shared_inbox = group.endpoints.shared_inbox.clone().map(|s| s.into());
|
|
||||||
|
|
||||||
let slur_regex = &settings.slur_regex();
|
let slur_regex = &context.settings().slur_regex();
|
||||||
check_slurs(&name, slur_regex)?;
|
check_slurs(&self.preferred_username, slur_regex)?;
|
||||||
check_slurs(&title, slur_regex)?;
|
check_slurs(&self.name, slur_regex)?;
|
||||||
|
let description = get_summary_from_string_or_source(&self.summary, &self.source);
|
||||||
check_slurs_opt(&description, slur_regex)?;
|
check_slurs_opt(&description, slur_regex)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
Ok(CommunityForm {
|
pub(crate) fn into_form(self) -> CommunityForm {
|
||||||
name,
|
CommunityForm {
|
||||||
title,
|
name: self.preferred_username,
|
||||||
description,
|
title: self.name,
|
||||||
|
description: get_summary_from_string_or_source(&self.summary, &self.source),
|
||||||
removed: None,
|
removed: None,
|
||||||
published: group.published.map(|u| u.naive_local()),
|
published: self.published.map(|u| u.naive_local()),
|
||||||
updated: group.updated.map(|u| u.naive_local()),
|
updated: self.updated.map(|u| u.naive_local()),
|
||||||
deleted: None,
|
deleted: None,
|
||||||
nsfw: Some(group.sensitive.unwrap_or(false)),
|
nsfw: Some(self.sensitive.unwrap_or(false)),
|
||||||
actor_id: Some(group.id.clone().into()),
|
actor_id: Some(self.id.into()),
|
||||||
local: Some(false),
|
local: Some(false),
|
||||||
private_key: None,
|
private_key: None,
|
||||||
public_key: Some(group.public_key.public_key_pem.clone()),
|
public_key: Some(self.public_key.public_key_pem),
|
||||||
last_refreshed_at: Some(naive_now()),
|
last_refreshed_at: Some(naive_now()),
|
||||||
icon: Some(group.icon.clone().map(|i| i.url.into())),
|
icon: Some(self.icon.map(|i| i.url.into())),
|
||||||
banner: Some(group.image.clone().map(|i| i.url.into())),
|
banner: Some(self.image.map(|i| i.url.into())),
|
||||||
followers_url: Some(group.followers.clone().into()),
|
followers_url: Some(self.followers.into()),
|
||||||
inbox_url: Some(group.inbox.clone().into()),
|
inbox_url: Some(self.inbox.into()),
|
||||||
shared_inbox_url: Some(shared_inbox),
|
shared_inbox_url: Some(self.endpoints.shared_inbox.map(|s| s.into())),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) mod chat_message;
|
pub(crate) mod chat_message;
|
||||||
pub(crate) mod group;
|
pub(crate) mod group;
|
||||||
pub(crate) mod note;
|
pub(crate) mod note;
|
||||||
|
@ -5,6 +8,13 @@ pub(crate) mod page;
|
||||||
pub(crate) mod person;
|
pub(crate) mod person;
|
||||||
pub(crate) mod tombstone;
|
pub(crate) mod tombstone;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Endpoints {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub shared_inbox: Option<Url>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{verify_is_public, verify_person_in_community},
|
fetcher::post_or_comment::PostOrComment,
|
||||||
fetcher::{object_id::ObjectId, post_or_comment::PostOrComment},
|
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
|
||||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
|
||||||
protocol::Source,
|
protocol::Source,
|
||||||
};
|
};
|
||||||
use activitystreams::{object::kind::NoteType, unparsed::Unparsed};
|
use activitystreams::{object::kind::NoteType, unparsed::Unparsed};
|
||||||
use anyhow::anyhow;
|
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{values::MediaTypeHtml, verify::verify_domains_match};
|
use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{newtypes::CommentId, source::post::Post, traits::Crud};
|
||||||
newtypes::CommentId,
|
|
||||||
source::{community::Community, post::Post},
|
|
||||||
traits::Crud,
|
|
||||||
};
|
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -26,12 +20,11 @@ use url::Url;
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Note {
|
pub struct Note {
|
||||||
pub(crate) r#type: NoteType,
|
pub(crate) r#type: NoteType,
|
||||||
pub(crate) id: Url,
|
pub(crate) id: ObjectId<ApubComment>,
|
||||||
pub(crate) attributed_to: ObjectId<ApubPerson>,
|
pub(crate) attributed_to: ObjectId<ApubPerson>,
|
||||||
/// Indicates that the object is publicly readable. Unlike [`Post.to`], this one doesn't contain
|
|
||||||
/// the community ID, as it would be incompatible with Pleroma (and we can get the community from
|
|
||||||
/// the post in [`in_reply_to`]).
|
|
||||||
pub(crate) to: Vec<Url>,
|
pub(crate) to: Vec<Url>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) cc: Vec<Url>,
|
||||||
pub(crate) content: String,
|
pub(crate) content: String,
|
||||||
pub(crate) media_type: Option<MediaTypeHtml>,
|
pub(crate) media_type: Option<MediaTypeHtml>,
|
||||||
pub(crate) source: SourceCompat,
|
pub(crate) source: SourceCompat,
|
||||||
|
@ -52,14 +45,6 @@ pub(crate) enum SourceCompat {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Note {
|
impl Note {
|
||||||
pub(crate) fn id_unchecked(&self) -> &Url {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
|
|
||||||
verify_domains_match(&self.id, expected_domain)?;
|
|
||||||
Ok(&self.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_parents(
|
pub(crate) async fn get_parents(
|
||||||
&self,
|
&self,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -87,26 +72,4 @@ impl Note {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn verify(
|
|
||||||
&self,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let (post, _parent_comment_id) = self.get_parents(context, request_counter).await?;
|
|
||||||
let community_id = post.community_id;
|
|
||||||
let community: ApubCommunity = blocking(context.pool(), move |conn| {
|
|
||||||
Community::read(conn, community_id)
|
|
||||||
})
|
|
||||||
.await??
|
|
||||||
.into();
|
|
||||||
|
|
||||||
if post.locked {
|
|
||||||
return Err(anyhow!("Post is locked").into());
|
|
||||||
}
|
|
||||||
verify_domains_match(self.attributed_to.inner(), &self.id)?;
|
|
||||||
verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?;
|
|
||||||
verify_is_public(&self.to)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{verify_is_public, verify_person_in_community},
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
protocol::{ImageObject, Source},
|
protocol::{ImageObject, Source},
|
||||||
};
|
};
|
||||||
use activitystreams::{object::kind::PageType, unparsed::Unparsed};
|
use activitystreams::{object::kind::PageType, unparsed::Unparsed};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use lemmy_apub_lib::{values::MediaTypeHtml, verify::verify_domains_match};
|
use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml};
|
||||||
use lemmy_utils::{utils::check_slurs, LemmyError};
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
|
@ -19,9 +17,11 @@ use url::Url;
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
pub(crate) r#type: PageType,
|
pub(crate) r#type: PageType,
|
||||||
pub(crate) id: Url,
|
pub(crate) id: ObjectId<ApubPost>,
|
||||||
pub(crate) attributed_to: ObjectId<ApubPerson>,
|
pub(crate) attributed_to: ObjectId<ApubPerson>,
|
||||||
pub(crate) to: Vec<Url>,
|
pub(crate) to: Vec<Url>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) cc: Vec<Url>,
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) content: Option<String>,
|
pub(crate) content: Option<String>,
|
||||||
pub(crate) media_type: Option<MediaTypeHtml>,
|
pub(crate) media_type: Option<MediaTypeHtml>,
|
||||||
|
@ -38,14 +38,6 @@ pub struct Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
pub(crate) fn id_unchecked(&self) -> &Url {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
|
|
||||||
verify_domains_match(&self.id, expected_domain)?;
|
|
||||||
Ok(&self.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Only mods can change the post's stickied/locked status. So if either of these is changed from
|
/// Only mods can change the post's stickied/locked status. So if either of these is changed from
|
||||||
/// the current value, it is a mod action and needs to be verified as such.
|
/// the current value, it is a mod action and needs to be verified as such.
|
||||||
///
|
///
|
||||||
|
@ -63,20 +55,6 @@ impl Page {
|
||||||
Ok(is_mod_action)
|
Ok(is_mod_action)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn verify(
|
|
||||||
&self,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let community = self.extract_community(context, request_counter).await?;
|
|
||||||
|
|
||||||
check_slurs(&self.name, &context.settings().slur_regex())?;
|
|
||||||
verify_domains_match(self.attributed_to.inner(), &self.id.clone())?;
|
|
||||||
verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?;
|
|
||||||
verify_is_public(&self.to.clone())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn extract_community(
|
pub(crate) async fn extract_community(
|
||||||
&self,
|
&self,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use crate::protocol::{ImageObject, Source};
|
use crate::{
|
||||||
use activitystreams::{actor::Endpoints, unparsed::Unparsed, url::Url};
|
objects::person::ApubPerson,
|
||||||
|
protocol::{objects::Endpoints, ImageObject, Source},
|
||||||
|
};
|
||||||
|
use activitystreams::{unparsed::Unparsed, url::Url};
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use lemmy_apub_lib::signatures::PublicKey;
|
use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
|
|
||||||
|
@ -17,7 +20,7 @@ pub enum UserTypes {
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub(crate) kind: UserTypes,
|
pub(crate) kind: UserTypes,
|
||||||
pub(crate) id: Url,
|
pub(crate) id: ObjectId<ApubPerson>,
|
||||||
/// username, set at account creation and can never be changed
|
/// username, set at account creation and can never be changed
|
||||||
pub(crate) preferred_username: String,
|
pub(crate) preferred_username: String,
|
||||||
/// displayname (can be changed at any time)
|
/// displayname (can be changed at any time)
|
||||||
|
@ -32,7 +35,7 @@ pub struct Person {
|
||||||
pub(crate) inbox: Url,
|
pub(crate) inbox: Url,
|
||||||
/// mandatory field in activitypub, currently empty in lemmy
|
/// mandatory field in activitypub, currently empty in lemmy
|
||||||
pub(crate) outbox: Url,
|
pub(crate) outbox: Url,
|
||||||
pub(crate) endpoints: Endpoints<Url>,
|
pub(crate) endpoints: Endpoints,
|
||||||
pub(crate) public_key: PublicKey,
|
pub(crate) public_key: PublicKey,
|
||||||
pub(crate) published: Option<DateTime<FixedOffset>>,
|
pub(crate) published: Option<DateTime<FixedOffset>>,
|
||||||
pub(crate) updated: Option<DateTime<FixedOffset>>,
|
pub(crate) updated: Option<DateTime<FixedOffset>>,
|
||||||
|
|
|
@ -27,3 +27,4 @@ actix-web = { version = "4.0.0-beta.9", default-features = false }
|
||||||
http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["server", "sha-2"] }
|
http-signature-normalization-actix = { version = "0.5.0-beta.10", default-features = false, features = ["server", "sha-2"] }
|
||||||
http-signature-normalization-reqwest = { version = "0.2.0", default-features = false, features = ["sha-2"] }
|
http-signature-normalization-reqwest = { version = "0.2.0", default-features = false, features = ["sha-2"] }
|
||||||
background-jobs = "0.9.1"
|
background-jobs = "0.9.1"
|
||||||
|
diesel = "1.4.8"
|
||||||
|
|
|
@ -3,6 +3,7 @@ extern crate lazy_static;
|
||||||
|
|
||||||
pub mod activity_queue;
|
pub mod activity_queue;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
pub mod object_id;
|
||||||
pub mod signatures;
|
pub mod signatures;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
pub mod values;
|
pub mod values;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::fetcher::should_refetch_object;
|
use crate::{traits::ApubObject, APUB_JSON_CONTENT_TYPE};
|
||||||
|
use activitystreams::chrono::{Duration as ChronoDuration, NaiveDateTime, Utc};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use diesel::NotFound;
|
use diesel::NotFound;
|
||||||
use lemmy_apub_lib::{traits::ApubObject, APUB_JSON_CONTENT_TYPE};
|
|
||||||
use lemmy_db_schema::newtypes::DbUrl;
|
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
request::{build_user_agent, retry},
|
request::{build_user_agent, retry},
|
||||||
settings::structs::Settings,
|
settings::structs::Settings,
|
||||||
|
@ -29,9 +28,10 @@ lazy_static! {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We store Url on the heap because it is quite large (88 bytes).
|
||||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
|
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct ObjectId<Kind>(Url, #[serde(skip)] PhantomData<Kind>)
|
pub struct ObjectId<Kind>(Box<Url>, #[serde(skip)] PhantomData<Kind>)
|
||||||
where
|
where
|
||||||
Kind: ApubObject + Send + 'static,
|
Kind: ApubObject + Send + 'static,
|
||||||
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>;
|
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>;
|
||||||
|
@ -45,7 +45,7 @@ where
|
||||||
where
|
where
|
||||||
T: Into<Url>,
|
T: Into<Url>,
|
||||||
{
|
{
|
||||||
ObjectId(url.into(), PhantomData::<Kind>)
|
ObjectId(Box::new(url.into()), PhantomData::<Kind>)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner(&self) -> &Url {
|
pub fn inner(&self) -> &Url {
|
||||||
|
@ -104,7 +104,7 @@ where
|
||||||
data: &<Kind as ApubObject>::DataType,
|
data: &<Kind as ApubObject>::DataType,
|
||||||
) -> Result<Option<Kind>, LemmyError> {
|
) -> Result<Option<Kind>, LemmyError> {
|
||||||
let id = self.0.clone();
|
let id = self.0.clone();
|
||||||
ApubObject::read_from_apub_id(id, data).await
|
ApubObject::read_from_apub_id(*id, data).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn dereference_from_http(
|
async fn dereference_from_http(
|
||||||
|
@ -140,16 +140,39 @@ where
|
||||||
|
|
||||||
let res2: Kind::ApubType = res.json().await?;
|
let res2: Kind::ApubType = res.json().await?;
|
||||||
|
|
||||||
Ok(Kind::from_apub(&res2, data, self.inner(), request_counter).await?)
|
Kind::verify(&res2, self.inner(), data, request_counter).await?;
|
||||||
|
Ok(Kind::from_apub(res2, data, request_counter).await?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ACTOR_REFETCH_INTERVAL_SECONDS: i64 = 24 * 60 * 60;
|
||||||
|
static ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG: i64 = 10;
|
||||||
|
|
||||||
|
/// Determines when a remote actor should be refetched from its instance. In release builds, this is
|
||||||
|
/// `ACTOR_REFETCH_INTERVAL_SECONDS` after the last refetch, in debug builds
|
||||||
|
/// `ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG`.
|
||||||
|
///
|
||||||
|
/// TODO it won't pick up new avatars, summaries etc until a day after.
|
||||||
|
/// Actors need an "update" activity pushed to other servers to fix this.
|
||||||
|
fn should_refetch_object(last_refreshed: NaiveDateTime) -> bool {
|
||||||
|
let update_interval = if cfg!(debug_assertions) {
|
||||||
|
// avoid infinite loop when fetching community outbox
|
||||||
|
ChronoDuration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG)
|
||||||
|
} else {
|
||||||
|
ChronoDuration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS)
|
||||||
|
};
|
||||||
|
let refresh_limit = Utc::now().naive_utc() - update_interval;
|
||||||
|
last_refreshed.lt(&refresh_limit)
|
||||||
|
}
|
||||||
|
|
||||||
impl<Kind> Display for ObjectId<Kind>
|
impl<Kind> Display for ObjectId<Kind>
|
||||||
where
|
where
|
||||||
Kind: ApubObject + Send + 'static,
|
Kind: ApubObject + Send + 'static,
|
||||||
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
||||||
{
|
{
|
||||||
|
#[allow(clippy::to_string_in_display)]
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// Use to_string here because Url.display is not useful for us
|
||||||
write!(f, "{}", self.0.to_string())
|
write!(f, "{}", self.0.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,16 +183,21 @@ where
|
||||||
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
||||||
{
|
{
|
||||||
fn from(id: ObjectId<Kind>) -> Self {
|
fn from(id: ObjectId<Kind>) -> Self {
|
||||||
id.0
|
*id.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Kind> From<ObjectId<Kind>> for DbUrl
|
#[cfg(test)]
|
||||||
where
|
mod tests {
|
||||||
Kind: ApubObject + Send + 'static,
|
use super::*;
|
||||||
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
use crate::object_id::should_refetch_object;
|
||||||
{
|
|
||||||
fn from(id: ObjectId<Kind>) -> Self {
|
#[test]
|
||||||
id.0.into()
|
fn test_should_refetch_object() {
|
||||||
|
let one_second_ago = Utc::now().naive_utc() - ChronoDuration::seconds(1);
|
||||||
|
assert!(!should_refetch_object(one_second_ago));
|
||||||
|
|
||||||
|
let two_days_ago = Utc::now().naive_utc() - ChronoDuration::days(2);
|
||||||
|
assert!(should_refetch_object(two_days_ago));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -91,7 +91,7 @@ pub fn verify_signature(request: &HttpRequest, public_key: &str) -> Result<(), L
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PublicKey {
|
pub struct PublicKey {
|
||||||
pub id: String,
|
pub(crate) id: String,
|
||||||
pub owner: Url,
|
pub(crate) owner: Box<Url>,
|
||||||
pub public_key_pem: String,
|
pub public_key_pem: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,6 @@ pub use lemmy_apub_lib_derive::*;
|
||||||
use lemmy_utils::{location_info, LemmyError};
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub trait ActivityFields {
|
|
||||||
fn id_unchecked(&self) -> &Url;
|
|
||||||
fn actor(&self) -> &Url;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
pub trait ActivityHandler {
|
pub trait ActivityHandler {
|
||||||
type DataType;
|
type DataType;
|
||||||
|
@ -46,9 +41,16 @@ pub trait ApubObject {
|
||||||
async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError>;
|
async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
/// Trait for converting an object or actor into the respective ActivityPub type.
|
/// Trait for converting an object or actor into the respective ActivityPub type.
|
||||||
async fn to_apub(&self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError>;
|
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError>;
|
||||||
fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError>;
|
fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError>;
|
||||||
|
|
||||||
|
async fn verify(
|
||||||
|
apub: &Self::ApubType,
|
||||||
|
expected_domain: &Url,
|
||||||
|
data: &Self::DataType,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
/// Converts an object from ActivityPub type to Lemmy internal type.
|
/// Converts an object from ActivityPub type to Lemmy internal type.
|
||||||
///
|
///
|
||||||
/// * `apub` The object to read from
|
/// * `apub` The object to read from
|
||||||
|
@ -56,9 +58,8 @@ pub trait ApubObject {
|
||||||
/// * `expected_domain` Domain where the object was received from. None in case of mod action.
|
/// * `expected_domain` Domain where the object was received from. None in case of mod action.
|
||||||
/// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
|
/// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
|
||||||
async fn from_apub(
|
async fn from_apub(
|
||||||
apub: &Self::ApubType,
|
apub: Self::ApubType,
|
||||||
data: &Self::DataType,
|
data: &Self::DataType,
|
||||||
expected_domain: &Url,
|
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<Self, LemmyError>
|
) -> Result<Self, LemmyError>
|
||||||
where
|
where
|
||||||
|
@ -68,9 +69,7 @@ pub trait ApubObject {
|
||||||
/// Common methods provided by ActivityPub actors (community and person). Not all methods are
|
/// Common methods provided by ActivityPub actors (community and person). Not all methods are
|
||||||
/// implemented by all actors.
|
/// implemented by all actors.
|
||||||
pub trait ActorType {
|
pub trait ActorType {
|
||||||
fn is_local(&self) -> bool;
|
|
||||||
fn actor_id(&self) -> Url;
|
fn actor_id(&self) -> Url;
|
||||||
fn name(&self) -> String;
|
|
||||||
|
|
||||||
// TODO: this should not be an option (needs db migration in lemmy)
|
// TODO: this should not be an option (needs db migration in lemmy)
|
||||||
fn public_key(&self) -> Option<String>;
|
fn public_key(&self) -> Option<String>;
|
||||||
|
@ -87,7 +86,7 @@ pub trait ActorType {
|
||||||
fn get_public_key(&self) -> Result<PublicKey, LemmyError> {
|
fn get_public_key(&self) -> Result<PublicKey, LemmyError> {
|
||||||
Ok(PublicKey {
|
Ok(PublicKey {
|
||||||
id: format!("{}#main-key", self.actor_id()),
|
id: format!("{}#main-key", self.actor_id()),
|
||||||
owner: self.actor_id(),
|
owner: Box::new(self.actor_id()),
|
||||||
public_key_pem: self.public_key().context(location_info!())?,
|
public_key_pem: self.public_key().context(location_info!())?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,40 +127,3 @@ fn generate_match_arm(enum_name: &Ident, variant: &Variant, body: &TokenStream)
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(ActivityFields)]
|
|
||||||
pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
|
|
||||||
let name = input.ident;
|
|
||||||
|
|
||||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
|
||||||
|
|
||||||
let expanded = match input.data {
|
|
||||||
Data::Enum(e) => {
|
|
||||||
let variants = e.variants;
|
|
||||||
let impl_id = variants
|
|
||||||
.iter()
|
|
||||||
.map(|v| generate_match_arm(&name, v, "e! {a.id_unchecked()}));
|
|
||||||
let impl_actor = variants
|
|
||||||
.iter()
|
|
||||||
.map(|v| generate_match_arm(&name, v, "e! {a.actor()}));
|
|
||||||
quote! {
|
|
||||||
impl #impl_generics lemmy_apub_lib::traits::ActivityFields for #name #ty_generics #where_clause {
|
|
||||||
fn id_unchecked(&self) -> &url::Url { match self { #(#impl_id)* } }
|
|
||||||
fn actor(&self) -> &url::Url { match self { #(#impl_actor)* } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Data::Struct(_) => {
|
|
||||||
quote! {
|
|
||||||
impl #impl_generics lemmy_apub_lib::traits::ActivityFields for #name #ty_generics #where_clause {
|
|
||||||
fn id_unchecked(&self) -> &url::Url { &self.id }
|
|
||||||
fn actor(&self) -> &url::Url { &self.actor.inner() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
expanded.into()
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lemmy_utils = { version = "=0.13.5-rc.7", path = "../utils" }
|
lemmy_utils = { version = "=0.13.5-rc.7", path = "../utils" }
|
||||||
|
lemmy_apub_lib = { version = "=0.13.5-rc.7", path = "../apub_lib" }
|
||||||
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
|
||||||
diesel_migrations = "1.4.0"
|
diesel_migrations = "1.4.0"
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
|
|
|
@ -4,6 +4,7 @@ use diesel::{
|
||||||
serialize::{Output, ToSql},
|
serialize::{Output, ToSql},
|
||||||
sql_types::Text,
|
sql_types::Text,
|
||||||
};
|
};
|
||||||
|
use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
|
@ -93,27 +94,32 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbUrl {
|
|
||||||
// TODO: remove this method and just use into()
|
|
||||||
pub fn into_inner(self) -> Url {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for DbUrl {
|
impl Display for DbUrl {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
self.to_owned().0.fmt(f)
|
self.to_owned().0.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DbUrl> for Url {
|
// the project doesnt compile with From
|
||||||
fn from(url: DbUrl) -> Self {
|
#[allow(clippy::from_over_into)]
|
||||||
url.0
|
impl Into<DbUrl> for Url {
|
||||||
|
fn into(self) -> DbUrl {
|
||||||
|
DbUrl(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[allow(clippy::from_over_into)]
|
||||||
|
impl Into<Url> for DbUrl {
|
||||||
|
fn into(self) -> Url {
|
||||||
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Url> for DbUrl {
|
impl<Kind> From<ObjectId<Kind>> for DbUrl
|
||||||
fn from(url: Url) -> Self {
|
where
|
||||||
DbUrl(url)
|
Kind: ApubObject + Send + 'static,
|
||||||
|
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
||||||
|
{
|
||||||
|
fn from(id: ObjectId<Kind>) -> Self {
|
||||||
|
DbUrl(id.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue