Refactoring apub code

This commit is contained in:
Felix Ableitner 2021-11-03 17:26:09 +01:00
parent a83113935d
commit 969a7f2d1b
13 changed files with 46 additions and 62 deletions

View file

@ -83,7 +83,7 @@ impl ActivityHandler for CreateOrUpdateComment {
verify_activity(self, &context.settings())?; verify_activity(self, &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)?;

View file

@ -84,7 +84,7 @@ impl ActivityHandler for CreateOrUpdatePost {
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(), 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
@ -101,7 +101,7 @@ 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(), self.object.attributed_to.inner())?;
} }
} }

View file

@ -56,7 +56,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?; verify_activity(self, &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?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }

View file

@ -39,6 +39,7 @@ use crate::{
}, },
PostOrComment, PostOrComment,
}; };
use lemmy_apub_lib::verify::verify_domains_match;
use lemmy_utils::utils::markdown_to_html; use lemmy_utils::utils::markdown_to_html;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -107,7 +108,7 @@ impl ApubObject for ApubComment {
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.to_owned()),
attributed_to: ObjectId::new(creator.actor_id), attributed_to: ObjectId::new(creator.actor_id),
to: vec![public()], to: vec![public()],
content: markdown_to_html(&self.content), content: markdown_to_html(&self.content),
@ -141,7 +142,8 @@ impl ApubObject for ApubComment {
expected_domain: &Url, expected_domain: &Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<ApubComment, LemmyError> { ) -> Result<ApubComment, LemmyError> {
let ap_id = Some(note.id(expected_domain)?.clone().into()); verify_domains_match(note.id.inner(), expected_domain)?;
let ap_id = Some(note.id.clone().into());
let creator = note let creator = note
.attributed_to .attributed_to
.dereference(context, request_counter) .dereference(context, request_counter)
@ -152,7 +154,7 @@ impl ApubObject for ApubComment {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??;
check_is_apub_id_valid(&note.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(
&note.attributed_to, &note.attributed_to,
&community.into(), &community.into(),

View file

@ -96,7 +96,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)),

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
fetcher::object_id::ObjectId,
generate_outbox_url, generate_outbox_url,
objects::get_summary_from_string_or_source, objects::get_summary_from_string_or_source,
protocol::{ protocol::{
@ -96,7 +97,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)),
@ -128,7 +129,7 @@ impl ApubObject for ApubPerson {
expected_domain: &Url, expected_domain: &Url,
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<ApubPerson, LemmyError> { ) -> Result<ApubPerson, LemmyError> {
verify_domains_match(&person.id, expected_domain)?; verify_domains_match(person.id.inner(), expected_domain)?;
let actor_id = Some(person.id.clone().into()); let actor_id = Some(person.id.clone().into());
let name = person.preferred_username.clone(); let name = person.preferred_username.clone();
let display_name: Option<String> = person.name.clone(); let display_name: Option<String> = person.name.clone();
@ -144,7 +145,7 @@ impl ApubObject for ApubPerson {
check_slurs_opt(&display_name, slur_regex)?; check_slurs_opt(&display_name, slur_regex)?;
check_slurs_opt(&bio, slur_regex)?; check_slurs_opt(&bio, slur_regex)?;
check_is_apub_id_valid(&person.id, false, &context.settings())?; check_is_apub_id_valid(person.id.inner(), false, &context.settings())?;
let person_form = PersonForm { let person_form = PersonForm {
name, name,

View file

@ -17,6 +17,7 @@ use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
traits::ApubObject, traits::ApubObject,
values::{MediaTypeHtml, MediaTypeMarkdown}, values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
self, self,
@ -106,7 +107,7 @@ 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()],
name: self.name.clone(), name: self.name.clone(),
@ -140,18 +141,16 @@ impl ApubObject for ApubPost {
) -> Result<ApubPost, LemmyError> { ) -> Result<ApubPost, 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 ap_id = Some(page.id.clone().into());
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())?; check_is_apub_id_valid(page.id.inner(), community.local, &context.settings())?;
verify_person_in_community(&page.attributed_to, &community, context, request_counter).await?; 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.clone().map(|i| i.url);

View file

@ -11,6 +11,7 @@ use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
traits::ApubObject, traits::ApubObject,
values::{MediaTypeHtml, MediaTypeMarkdown}, values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
@ -81,7 +82,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),
@ -107,7 +108,8 @@ impl ApubObject for ApubPrivateMessage {
expected_domain: &Url, expected_domain: &Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<ApubPrivateMessage, LemmyError> { ) -> Result<ApubPrivateMessage, LemmyError> {
let ap_id = Some(note.id(expected_domain)?.clone().into()); verify_domains_match(note.id.inner(), expected_domain)?;
let ap_id = Some(note.id.clone().into());
let creator = note let creator = note
.attributed_to .attributed_to
.dereference(context, request_counter) .dereference(context, request_counter)

View file

@ -1,4 +1,8 @@
use crate::{fetcher::object_id::ObjectId, objects::person::ApubPerson, protocol::Source}; use crate::{
fetcher::object_id::ObjectId,
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::Source,
};
use activitystreams::{ use activitystreams::{
chrono::{DateTime, FixedOffset}, chrono::{DateTime, FixedOffset},
unparsed::Unparsed, unparsed::Unparsed,
@ -9,14 +13,13 @@ 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;
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,
@ -35,20 +38,12 @@ pub enum ChatMessageType {
} }
impl 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( pub(crate) async fn verify(
&self, &self,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(self.attributed_to.inner(), &self.id)?; verify_domains_match(self.attributed_to.inner(), self.id.inner())?;
let person = self let person = self
.attributed_to .attributed_to
.dereference(context, request_counter) .dereference(context, request_counter)

View file

@ -5,7 +5,7 @@ use crate::{
community_outbox::ApubCommunityOutbox, community_outbox::ApubCommunityOutbox,
}, },
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::get_summary_from_string_or_source, objects::{community::ApubCommunity, get_summary_from_string_or_source},
protocol::{ImageObject, Source}, protocol::{ImageObject, Source},
}; };
use activitystreams::{ use activitystreams::{
@ -30,7 +30,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)
@ -61,8 +61,8 @@ impl Group {
expected_domain: &Url, expected_domain: &Url,
settings: &Settings, settings: &Settings,
) -> Result<CommunityForm, LemmyError> { ) -> Result<CommunityForm, LemmyError> {
check_is_apub_id_valid(&group.id, true, settings)?; check_is_apub_id_valid(group.id.inner(), true, settings)?;
verify_domains_match(expected_domain, &group.id)?; verify_domains_match(expected_domain, group.id.inner())?;
let name = group.preferred_username.clone(); let name = group.preferred_username.clone();
let title = group.name.clone(); let title = group.name.clone();
let description = get_summary_from_string_or_source(&group.summary, &group.source); let description = get_summary_from_string_or_source(&group.summary, &group.source);

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
activities::{verify_is_public, verify_person_in_community}, activities::{verify_is_public, verify_person_in_community},
fetcher::{object_id::ObjectId, post_or_comment::PostOrComment}, fetcher::{object_id::ObjectId, post_or_comment::PostOrComment},
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::Source, protocol::Source,
}; };
use activitystreams::{object::kind::NoteType, unparsed::Unparsed}; use activitystreams::{object::kind::NoteType, unparsed::Unparsed};
@ -26,11 +26,8 @@ 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>,
pub(crate) content: String, pub(crate) content: String,
pub(crate) media_type: Option<MediaTypeHtml>, pub(crate) media_type: Option<MediaTypeHtml>,
@ -52,14 +49,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,
@ -104,7 +93,7 @@ impl Note {
if post.locked { if post.locked {
return Err(anyhow!("Post is locked").into()); return Err(anyhow!("Post is locked").into());
} }
verify_domains_match(self.attributed_to.inner(), &self.id)?; verify_domains_match(self.attributed_to.inner(), self.id.inner())?;
verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?;
verify_is_public(&self.to)?; verify_is_public(&self.to)?;
Ok(()) Ok(())

View file

@ -19,7 +19,7 @@ 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>,
pub(crate) name: String, pub(crate) name: String,
@ -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.
/// ///
@ -71,7 +63,7 @@ impl Page {
let community = self.extract_community(context, request_counter).await?; let community = self.extract_community(context, request_counter).await?;
check_slurs(&self.name, &context.settings().slur_regex())?; check_slurs(&self.name, &context.settings().slur_regex())?;
verify_domains_match(self.attributed_to.inner(), &self.id.clone())?; verify_domains_match(self.attributed_to.inner(), self.id.inner())?;
verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?; verify_person_in_community(&self.attributed_to, &community, context, request_counter).await?;
verify_is_public(&self.to.clone())?; verify_is_public(&self.to.clone())?;
Ok(()) Ok(())

View file

@ -1,4 +1,8 @@
use crate::protocol::{ImageObject, Source}; use crate::{
fetcher::object_id::ObjectId,
objects::person::ApubPerson,
protocol::{ImageObject, Source},
};
use activitystreams::{actor::Endpoints, unparsed::Unparsed, url::Url}; use activitystreams::{actor::Endpoints, unparsed::Unparsed, url::Url};
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::signatures::PublicKey; use lemmy_apub_lib::signatures::PublicKey;
@ -17,7 +21,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)