]> Untitled Git - lemmy.git/commitdiff
Add security checks and slur checks for activitypub inbox
authorFelix Ableitner <me@nutomic.com>
Thu, 6 Aug 2020 12:53:58 +0000 (14:53 +0200)
committerFelix Ableitner <me@nutomic.com>
Thu, 6 Aug 2020 13:01:42 +0000 (15:01 +0200)
21 files changed:
server/lemmy_db/src/comment.rs
server/lemmy_db/src/community.rs
server/lemmy_db/src/post.rs
server/src/api/mod.rs
server/src/apub/comment.rs
server/src/apub/community.rs
server/src/apub/fetcher.rs
server/src/apub/inbox/activities/announce.rs
server/src/apub/inbox/activities/create.rs
server/src/apub/inbox/activities/delete.rs
server/src/apub/inbox/activities/dislike.rs
server/src/apub/inbox/activities/like.rs
server/src/apub/inbox/activities/remove.rs
server/src/apub/inbox/activities/undo.rs
server/src/apub/inbox/activities/update.rs
server/src/apub/inbox/shared_inbox.rs
server/src/apub/inbox/user_inbox.rs
server/src/apub/mod.rs
server/src/apub/post.rs
server/src/apub/private_message.rs
server/src/apub/user.rs

index 99efde8d7d1faaadb5430c12ba6c7e46f862da67..cdb5a1b6456822d2d2579d98d2579bbc883293a2 100644 (file)
@@ -116,10 +116,7 @@ impl Comment {
   ) -> Result<Self, Error> {
     use crate::schema::comment::dsl::*;
     diesel::update(comment.find(comment_id))
-      .set((
-        deleted.eq(new_deleted),
-        updated.eq(naive_now())
-      ))
+      .set((deleted.eq(new_deleted), updated.eq(naive_now())))
       .get_result::<Self>(conn)
   }
 
@@ -130,10 +127,7 @@ impl Comment {
   ) -> Result<Self, Error> {
     use crate::schema::comment::dsl::*;
     diesel::update(comment.find(comment_id))
-      .set((
-        removed.eq(new_removed),
-        updated.eq(naive_now())
-      ))
+      .set((removed.eq(new_removed), updated.eq(naive_now())))
       .get_result::<Self>(conn)
   }
 
index 2c86f1e755f0031160fca1eb57437ad55d70cfe6..14e8f98498fed16ec145a9b2d061a959463a3771 100644 (file)
@@ -107,10 +107,7 @@ impl Community {
   ) -> Result<Self, Error> {
     use crate::schema::community::dsl::*;
     diesel::update(community.find(community_id))
-      .set((
-        deleted.eq(new_deleted),
-        updated.eq(naive_now())
-      ))
+      .set((deleted.eq(new_deleted), updated.eq(naive_now())))
       .get_result::<Self>(conn)
   }
 
@@ -121,10 +118,7 @@ impl Community {
   ) -> Result<Self, Error> {
     use crate::schema::community::dsl::*;
     diesel::update(community.find(community_id))
-      .set((
-        removed.eq(new_removed),
-        updated.eq(naive_now())
-      ))
+      .set((removed.eq(new_removed), updated.eq(naive_now())))
       .get_result::<Self>(conn)
   }
 
index 56ff7474bdbb6e191b52ef56932235d873804e07..43e002113cec204a25aa2bc7fcc5d13ccbc267ee 100644 (file)
@@ -119,10 +119,7 @@ impl Post {
   ) -> Result<Self, Error> {
     use crate::schema::post::dsl::*;
     diesel::update(post.find(post_id))
-      .set((
-        deleted.eq(new_deleted),
-        updated.eq(naive_now())
-      ))
+      .set((deleted.eq(new_deleted), updated.eq(naive_now())))
       .get_result::<Self>(conn)
   }
 
@@ -133,10 +130,7 @@ impl Post {
   ) -> Result<Self, Error> {
     use crate::schema::post::dsl::*;
     diesel::update(post.find(post_id))
-      .set((
-        removed.eq(new_removed),
-        updated.eq(naive_now())
-      ))
+      .set((removed.eq(new_removed), updated.eq(naive_now())))
       .get_result::<Self>(conn)
   }
 
index a9aae823a5b53cee0c930ef58b25b9750375a7f9..7c5eeb2faf4431fa1224325cd6adaeb12a06ecc1 100644 (file)
@@ -65,6 +65,7 @@ pub async fn is_mod_or_admin(
   })
   .await?;
   if !is_mod_or_admin {
+    // TODO: more accurately, not_a_mod_or_admin?
     return Err(APIError::err("not_an_admin").into());
   }
   Ok(())
@@ -104,14 +105,14 @@ pub(in crate::api) async fn get_user_from_jwt_opt(
   }
 }
 
-pub(in crate::api) fn check_slurs(text: &str) -> Result<(), APIError> {
+pub(in crate) fn check_slurs(text: &str) -> Result<(), APIError> {
   if let Err(slurs) = slur_check(text) {
     Err(APIError::err(&slurs_vec_to_str(slurs)))
   } else {
     Ok(())
   }
 }
-pub(in crate::api) fn check_slurs_opt(text: &Option<String>) -> Result<(), APIError> {
+pub(in crate) fn check_slurs_opt(text: &Option<String>) -> Result<(), APIError> {
   match text {
     Some(t) => check_slurs(t),
     None => Ok(()),
index 8bd79b799f645f481ab5792829fe6dfd57aa17a3..1aa3790ccf7343e8bce489256abcb2fed0fd1fde 100644 (file)
@@ -1,7 +1,8 @@
 use crate::{
+  api::check_slurs,
   apub::{
     activities::{generate_activity_id, send_activity_to_community},
-    check_is_apub_id_valid,
+    check_actor_domain,
     create_apub_response,
     create_apub_tombstone_response,
     create_tombstone,
@@ -132,6 +133,7 @@ impl FromApub for CommentForm {
     note: &Note,
     client: &Client,
     pool: &DbPool,
+    expected_domain: Option<Url>,
   ) -> Result<CommentForm, LemmyError> {
     let creator_actor_id = &note
       .attributed_to()
@@ -166,26 +168,25 @@ impl FromApub for CommentForm {
       }
       None => None,
     };
-
-    let ap_id = note.id_unchecked().unwrap().to_string();
-    check_is_apub_id_valid(&Url::parse(&ap_id)?)?;
+    let content = note
+      .content()
+      .unwrap()
+      .as_single_xsd_string()
+      .unwrap()
+      .to_string();
+    check_slurs(&content)?;
 
     Ok(CommentForm {
       creator_id: creator.id,
       post_id: post.id,
       parent_id,
-      content: note
-        .content()
-        .unwrap()
-        .as_single_xsd_string()
-        .unwrap()
-        .to_string(),
+      content,
       removed: None,
       read: None,
       published: note.published().map(|u| u.to_owned().naive_local()),
       updated: note.updated().map(|u| u.to_owned().naive_local()),
       deleted: None,
-      ap_id,
+      ap_id: check_actor_domain(note, expected_domain)?,
       local: false,
     })
   }
index 44e3ad4e655290bbd54e7bacb873fab0c0159c23..3773b8fb4d5ab59faeb47961fa84ddfb75e0e4ce 100644 (file)
@@ -1,7 +1,8 @@
 use crate::{
+  api::{check_slurs, check_slurs_opt},
   apub::{
     activities::{generate_activity_id, send_activity},
-    check_is_apub_id_valid,
+    check_actor_domain,
     create_apub_response,
     create_apub_tombstone_response,
     create_tombstone,
@@ -323,7 +324,12 @@ impl FromApub for CommunityForm {
   type ApubType = GroupExt;
 
   /// Parse an ActivityPub group received from another instance into a Lemmy community.
-  async fn from_apub(group: &GroupExt, client: &Client, pool: &DbPool) -> Result<Self, LemmyError> {
+  async fn from_apub(
+    group: &GroupExt,
+    client: &Client,
+    pool: &DbPool,
+    expected_domain: Option<Url>,
+  ) -> Result<Self, LemmyError> {
     let creator_and_moderator_uris = group.inner.attributed_to().unwrap();
     let creator_uri = creator_and_moderator_uris
       .as_many()
@@ -335,26 +341,30 @@ impl FromApub for CommunityForm {
       .unwrap();
 
     let creator = get_or_fetch_and_upsert_user(creator_uri, client, pool).await?;
-    let actor_id = group.inner.id_unchecked().unwrap().to_string();
-    check_is_apub_id_valid(&Url::parse(&actor_id)?)?;
+    let name = group
+      .inner
+      .name()
+      .unwrap()
+      .as_one()
+      .unwrap()
+      .as_xsd_string()
+      .unwrap()
+      .to_string();
+    let title = group.inner.preferred_username().unwrap().to_string();
+    // TODO: should be parsed as html and tags like <script> removed (or use markdown source)
+    //       -> same for post.content etc
+    let description = group
+      .inner
+      .content()
+      .map(|s| s.as_single_xsd_string().unwrap().into());
+    check_slurs(&name)?;
+    check_slurs(&title)?;
+    check_slurs_opt(&description)?;
 
     Ok(CommunityForm {
-      name: group
-        .inner
-        .name()
-        .unwrap()
-        .as_one()
-        .unwrap()
-        .as_xsd_string()
-        .unwrap()
-        .into(),
-      title: group.inner.preferred_username().unwrap().to_string(),
-      // TODO: should be parsed as html and tags like <script> removed (or use markdown source)
-      //       -> same for post.content etc
-      description: group
-        .inner
-        .content()
-        .map(|s| s.as_single_xsd_string().unwrap().into()),
+      name,
+      title,
+      description,
       category_id: group.ext_one.category.identifier.parse::<i32>()?,
       creator_id: creator.id,
       removed: None,
@@ -362,7 +372,7 @@ impl FromApub for CommunityForm {
       updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
       deleted: None,
       nsfw: group.ext_one.sensitive,
-      actor_id,
+      actor_id: check_actor_domain(group, expected_domain)?,
       local: false,
       private_key: None,
       public_key: Some(group.ext_two.to_owned().public_key.public_key_pem),
index 919b0e884aa9a2eb82b0824473ffd49c29e8b613..5395dded7380dc783fa5b3f268a80aa4a17a1855 100644 (file)
@@ -172,7 +172,7 @@ pub async fn search_by_apub_id(
       response
     }
     SearchAcceptedObjects::Page(p) => {
-      let post_form = PostForm::from_apub(&p, client, pool).await?;
+      let post_form = PostForm::from_apub(&p, client, pool, Some(query_url)).await?;
 
       let p = blocking(pool, move |conn| upsert_post(&post_form, conn)).await??;
       response.posts = vec![blocking(pool, move |conn| PostView::read(conn, p.id, None)).await??];
@@ -185,8 +185,8 @@ pub async fn search_by_apub_id(
       // TODO: also fetch parent comments if any
       let x = post_url.first().unwrap().as_xsd_any_uri().unwrap();
       let post = fetch_remote_object(client, x).await?;
-      let post_form = PostForm::from_apub(&post, client, pool).await?;
-      let comment_form = CommentForm::from_apub(&c, client, pool).await?;
+      let post_form = PostForm::from_apub(&post, client, pool, Some(query_url.clone())).await?;
+      let comment_form = CommentForm::from_apub(&c, client, pool, Some(query_url)).await?;
 
       blocking(pool, move |conn| upsert_post(&post_form, conn)).await??;
       let c = blocking(pool, move |conn| upsert_comment(&comment_form, conn)).await??;
@@ -221,7 +221,7 @@ pub async fn get_or_fetch_and_upsert_user(
 ) -> Result<User_, LemmyError> {
   let apub_id_owned = apub_id.to_owned();
   let user = blocking(pool, move |conn| {
-    User_::read_from_actor_id(conn, apub_id_owned.as_str())
+    User_::read_from_actor_id(conn, apub_id_owned.as_ref())
   })
   .await?;
 
@@ -231,7 +231,7 @@ pub async fn get_or_fetch_and_upsert_user(
       debug!("Fetching and updating from remote user: {}", apub_id);
       let person = fetch_remote_object::<PersonExt>(client, apub_id).await?;
 
-      let mut uf = UserForm::from_apub(&person, client, pool).await?;
+      let mut uf = UserForm::from_apub(&person, client, pool, Some(apub_id.to_owned())).await?;
       uf.last_refreshed_at = Some(naive_now());
       let user = blocking(pool, move |conn| User_::update(conn, u.id, &uf)).await??;
 
@@ -242,7 +242,7 @@ pub async fn get_or_fetch_and_upsert_user(
       debug!("Fetching and creating remote user: {}", apub_id);
       let person = fetch_remote_object::<PersonExt>(client, apub_id).await?;
 
-      let uf = UserForm::from_apub(&person, client, pool).await?;
+      let uf = UserForm::from_apub(&person, client, pool, Some(apub_id.to_owned())).await?;
       let user = blocking(pool, move |conn| User_::create(conn, &uf)).await??;
 
       Ok(user)
@@ -300,7 +300,7 @@ async fn fetch_remote_community(
 ) -> Result<Community, LemmyError> {
   let group = fetch_remote_object::<GroupExt>(client, apub_id).await?;
 
-  let cf = CommunityForm::from_apub(&group, client, pool).await?;
+  let cf = CommunityForm::from_apub(&group, client, pool, Some(apub_id.to_owned())).await?;
   let community = blocking(pool, move |conn| {
     if let Some(cid) = community_id {
       Community::update(conn, cid, &cf)
@@ -350,7 +350,7 @@ async fn fetch_remote_community(
   let outbox_items = outbox.items().unwrap().clone();
   for o in outbox_items.many().unwrap() {
     let page = PageExt::from_any_base(o)?.unwrap();
-    let post = PostForm::from_apub(&page, client, pool).await?;
+    let post = PostForm::from_apub(&page, client, pool, Some(apub_id.to_owned())).await?;
     let post_ap_id = post.ap_id.clone();
     // Check whether the post already exists in the local db
     let existing = blocking(pool, move |conn| Post::read_from_apub_id(conn, &post_ap_id)).await?;
@@ -388,7 +388,7 @@ pub async fn get_or_fetch_and_insert_post(
     Err(NotFound {}) => {
       debug!("Fetching and creating remote post: {}", post_ap_id);
       let post = fetch_remote_object::<PageExt>(client, post_ap_id).await?;
-      let post_form = PostForm::from_apub(&post, client, pool).await?;
+      let post_form = PostForm::from_apub(&post, client, pool, Some(post_ap_id.to_owned())).await?;
 
       let post = blocking(pool, move |conn| Post::create(conn, &post_form)).await??;
 
@@ -426,7 +426,8 @@ pub async fn get_or_fetch_and_insert_comment(
         comment_ap_id
       );
       let comment = fetch_remote_object::<Note>(client, comment_ap_id).await?;
-      let comment_form = CommentForm::from_apub(&comment, client, pool).await?;
+      let comment_form =
+        CommentForm::from_apub(&comment, client, pool, Some(comment_ap_id.to_owned())).await?;
 
       let comment = blocking(pool, move |conn| Comment::create(conn, &comment_form)).await??;
 
index 8ca4856feffc4695134ca684f312e9d67d526668..95c4c8cd99c4880e482e690aa8bda741877a94f9 100644 (file)
@@ -1,21 +1,28 @@
 use crate::{
-  apub::inbox::{
-    activities::{
-      create::receive_create,
-      delete::receive_delete,
-      dislike::receive_dislike,
-      like::receive_like,
-      remove::receive_remove,
-      undo::receive_undo,
-      update::receive_update,
+  apub::{
+    inbox::{
+      activities::{
+        create::receive_create,
+        delete::receive_delete,
+        dislike::receive_dislike,
+        like::receive_like,
+        remove::receive_remove,
+        undo::receive_undo,
+        update::receive_update,
+      },
+      shared_inbox::{get_community_from_activity, receive_unhandled_activity},
     },
-    shared_inbox::receive_unhandled_activity,
+    ActorType,
   },
   routes::ChatServerParam,
   DbPool,
   LemmyError,
 };
-use activitystreams::{activity::*, base::AnyBase, prelude::ExtendsExt};
+use activitystreams::{
+  activity::*,
+  base::{AnyBase, BaseExt},
+  prelude::ExtendsExt,
+};
 use actix_web::{client::Client, HttpResponse};
 
 pub async fn receive_announce(
@@ -25,6 +32,11 @@ pub async fn receive_announce(
   chat_server: ChatServerParam,
 ) -> Result<HttpResponse, LemmyError> {
   let announce = Announce::from_any_base(activity)?.unwrap();
+
+  // ensure that announce and community come from the same instance
+  let community = get_community_from_activity(&announce, client, pool).await?;
+  announce.id(community.actor_id()?.domain().unwrap())?;
+
   let kind = announce.object().as_single_kind_str();
   let object = announce.object();
   let object2 = object.clone().one().unwrap();
index ceeef0ef76bfe2acba82ffc7ba430208cd5c6ae7..6e201ff3792804c320cad188b79323736a5b664a 100644 (file)
@@ -9,6 +9,7 @@ use crate::{
       get_user_from_activity,
       receive_unhandled_activity,
     },
+    ActorType,
     FromApub,
     PageExt,
   },
@@ -23,6 +24,7 @@ use crate::{
 };
 use activitystreams::{activity::Create, base::AnyBase, object::Note, prelude::*};
 use actix_web::{client::Client, HttpResponse};
+use anyhow::anyhow;
 use lemmy_db::{
   comment::{Comment, CommentForm},
   comment_view::CommentView,
@@ -39,6 +41,11 @@ pub async fn receive_create(
   chat_server: ChatServerParam,
 ) -> Result<HttpResponse, LemmyError> {
   let create = Create::from_any_base(activity)?.unwrap();
+
+  // ensure that create and actor come from the same instance
+  let user = get_user_from_activity(&create, client, pool).await?;
+  create.id(user.actor_id()?.domain().unwrap())?;
+
   match create.object().as_single_kind_str() {
     Some("Page") => receive_create_post(create, client, pool, chat_server).await,
     Some("Note") => receive_create_comment(create, client, pool, chat_server).await,
@@ -55,7 +62,11 @@ async fn receive_create_post(
   let user = get_user_from_activity(&create, client, pool).await?;
   let page = PageExt::from_any_base(create.object().to_owned().one().unwrap())?.unwrap();
 
-  let post = PostForm::from_apub(&page, client, pool).await?;
+  let post = PostForm::from_apub(&page, client, pool, Some(user.actor_id()?)).await?;
+  // TODO: not sure if it makes sense to check for the exact user, seeing as we already check the domain
+  if post.creator_id != user.id {
+    return Err(anyhow!("Actor for create activity and post creator need to be identical").into());
+  }
 
   let inserted_post = blocking(pool, move |conn| Post::create(conn, &post)).await??;
 
@@ -87,7 +98,12 @@ async fn receive_create_comment(
   let user = get_user_from_activity(&create, client, pool).await?;
   let note = Note::from_any_base(create.object().to_owned().one().unwrap())?.unwrap();
 
-  let comment = CommentForm::from_apub(&note, client, pool).await?;
+  let comment = CommentForm::from_apub(&note, client, pool, Some(user.actor_id()?)).await?;
+  if comment.creator_id != user.id {
+    return Err(
+      anyhow!("Actor for create activity and comment creator need to be identical").into(),
+    );
+  }
 
   let inserted_comment = blocking(pool, move |conn| Comment::create(conn, &comment)).await??;
 
index 4fb56d321eadcf1414a9109b5723d9c2c6055a27..223f9eb4e23927615bb59801c90fbe281fe45bef 100644 (file)
@@ -7,6 +7,7 @@ use crate::{
       get_user_from_activity,
       receive_unhandled_activity,
     },
+    ActorType,
     FromApub,
     GroupExt,
     PageExt,
@@ -57,7 +58,7 @@ async fn receive_delete_post(
   let user = get_user_from_activity(&delete, client, pool).await?;
   let page = PageExt::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
 
-  let post_ap_id = PostForm::from_apub(&page, client, pool)
+  let post_ap_id = PostForm::from_apub(&page, client, pool, Some(user.actor_id()?))
     .await?
     .get_ap_id()?;
 
@@ -111,7 +112,7 @@ async fn receive_delete_comment(
   let user = get_user_from_activity(&delete, client, pool).await?;
   let note = Note::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
 
-  let comment_ap_id = CommentForm::from_apub(&note, client, pool)
+  let comment_ap_id = CommentForm::from_apub(&note, client, pool, Some(user.actor_id()?))
     .await?
     .get_ap_id()?;
 
@@ -168,7 +169,7 @@ async fn receive_delete_community(
   let group = GroupExt::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
   let user = get_user_from_activity(&delete, client, pool).await?;
 
-  let community_actor_id = CommunityForm::from_apub(&group, client, pool)
+  let community_actor_id = CommunityForm::from_apub(&group, client, pool, Some(user.actor_id()?))
     .await?
     .actor_id;
 
index 1e67d192231cf0cf0619fabdfaf524b494bcf01a..95e2438eff9ba7e07c57d7ad8958b98914aa894c 100644 (file)
@@ -52,7 +52,7 @@ async fn receive_dislike_post(
   let user = get_user_from_activity(&dislike, client, pool).await?;
   let page = PageExt::from_any_base(dislike.object().to_owned().one().unwrap())?.unwrap();
 
-  let post = PostForm::from_apub(&page, client, pool).await?;
+  let post = PostForm::from_apub(&page, client, pool, None).await?;
 
   let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, client, pool)
     .await?
@@ -93,7 +93,7 @@ async fn receive_dislike_comment(
   let note = Note::from_any_base(dislike.object().to_owned().one().unwrap())?.unwrap();
   let user = get_user_from_activity(&dislike, client, pool).await?;
 
-  let comment = CommentForm::from_apub(&note, client, pool).await?;
+  let comment = CommentForm::from_apub(&note, client, pool, None).await?;
 
   let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, client, pool)
     .await?
index 9061773d0cc60db678884b74ee5c3eaf24f90403..f2705f222f76461d1b68d22434bf7f2500c82116 100644 (file)
@@ -52,7 +52,7 @@ async fn receive_like_post(
   let user = get_user_from_activity(&like, client, pool).await?;
   let page = PageExt::from_any_base(like.object().to_owned().one().unwrap())?.unwrap();
 
-  let post = PostForm::from_apub(&page, client, pool).await?;
+  let post = PostForm::from_apub(&page, client, pool, None).await?;
 
   let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, client, pool)
     .await?
@@ -93,7 +93,7 @@ async fn receive_like_comment(
   let note = Note::from_any_base(like.object().to_owned().one().unwrap())?.unwrap();
   let user = get_user_from_activity(&like, client, pool).await?;
 
-  let comment = CommentForm::from_apub(&note, client, pool).await?;
+  let comment = CommentForm::from_apub(&note, client, pool, None).await?;
 
   let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, client, pool)
     .await?
index 485d28616bad352c961285b2962d6288261c76ed..3db019fe2918645c3b945b0a16a28335e61535b9 100644 (file)
@@ -1,12 +1,19 @@
 use crate::{
-  api::{comment::CommentResponse, community::CommunityResponse, post::PostResponse},
+  api::{
+    comment::CommentResponse,
+    community::CommunityResponse,
+    is_mod_or_admin,
+    post::PostResponse,
+  },
   apub::{
     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
     inbox::shared_inbox::{
       announce_if_community_is_local,
+      get_community_from_activity,
       get_user_from_activity,
       receive_unhandled_activity,
     },
+    ActorType,
     FromApub,
     GroupExt,
     PageExt,
@@ -40,6 +47,11 @@ pub async fn receive_remove(
   chat_server: ChatServerParam,
 ) -> Result<HttpResponse, LemmyError> {
   let remove = Remove::from_any_base(activity)?.unwrap();
+  let actor = get_user_from_activity(&remove, client, pool).await?;
+  let community = get_community_from_activity(&remove, client, pool).await?;
+  // TODO: we dont federate remote admins at all, and remote mods arent federated properly
+  is_mod_or_admin(pool, actor.id, community.id).await?;
+
   match remove.object().as_single_kind_str() {
     Some("Page") => receive_remove_post(remove, client, pool, chat_server).await,
     Some("Note") => receive_remove_comment(remove, client, pool, chat_server).await,
@@ -57,7 +69,7 @@ async fn receive_remove_post(
   let mod_ = get_user_from_activity(&remove, client, pool).await?;
   let page = PageExt::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
 
-  let post_ap_id = PostForm::from_apub(&page, client, pool)
+  let post_ap_id = PostForm::from_apub(&page, client, pool, None)
     .await?
     .get_ap_id()?;
 
@@ -111,7 +123,7 @@ async fn receive_remove_comment(
   let mod_ = get_user_from_activity(&remove, client, pool).await?;
   let note = Note::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
 
-  let comment_ap_id = CommentForm::from_apub(&note, client, pool)
+  let comment_ap_id = CommentForm::from_apub(&note, client, pool, None)
     .await?
     .get_ap_id()?;
 
@@ -168,7 +180,7 @@ async fn receive_remove_community(
   let mod_ = get_user_from_activity(&remove, client, pool).await?;
   let group = GroupExt::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
 
-  let community_actor_id = CommunityForm::from_apub(&group, client, pool)
+  let community_actor_id = CommunityForm::from_apub(&group, client, pool, Some(mod_.actor_id()?))
     .await?
     .actor_id;
 
index edfcf372ade4d51bfb6782c303c15053a4302db9..238b1bb0f3bbf120a8328ff17c28d893ee53f04f 100644 (file)
@@ -7,6 +7,7 @@ use crate::{
       get_user_from_activity,
       receive_unhandled_activity,
     },
+    ActorType,
     FromApub,
     GroupExt,
     PageExt,
@@ -20,7 +21,12 @@ use crate::{
   DbPool,
   LemmyError,
 };
-use activitystreams::{activity::*, base::AnyBase, object::Note, prelude::*};
+use activitystreams::{
+  activity::*,
+  base::{AnyBase, AsBase},
+  object::Note,
+  prelude::*,
+};
 use actix_web::{client::Client, HttpResponse};
 use anyhow::anyhow;
 use lemmy_db::{
@@ -47,11 +53,27 @@ pub async fn receive_undo(
     Some("Remove") => receive_undo_remove(undo, client, pool, chat_server).await,
     Some("Like") => receive_undo_like(undo, client, pool, chat_server).await,
     Some("Dislike") => receive_undo_dislike(undo, client, pool, chat_server).await,
-    // TODO: handle undo_dislike?
     _ => receive_unhandled_activity(undo),
   }
 }
 
+fn check_is_undo_valid<T, A>(outer_activity: &Undo, inner_activity: &T) -> Result<(), LemmyError>
+where
+  T: AsBase<A> + ActorAndObjectRef,
+{
+  let outer_actor = outer_activity.actor()?;
+  let outer_actor_uri = outer_actor.as_single_xsd_any_uri().unwrap();
+
+  let inner_actor = inner_activity.actor()?;
+  let inner_actor_uri = inner_actor.as_single_xsd_any_uri().unwrap();
+
+  if outer_actor_uri != inner_actor_uri {
+    Err(anyhow!("An actor can only undo its own activities").into())
+  } else {
+    Ok(())
+  }
+}
+
 async fn receive_undo_delete(
   undo: Undo,
   client: &Client,
@@ -59,6 +81,7 @@ async fn receive_undo_delete(
   chat_server: ChatServerParam,
 ) -> Result<HttpResponse, LemmyError> {
   let delete = Delete::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
+  check_is_undo_valid(&undo, &delete)?;
   let type_ = delete.object().as_single_kind_str().unwrap();
   match type_ {
     "Note" => receive_undo_delete_comment(undo, &delete, client, pool, chat_server).await,
@@ -75,6 +98,7 @@ async fn receive_undo_remove(
   chat_server: ChatServerParam,
 ) -> Result<HttpResponse, LemmyError> {
   let remove = Remove::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
+  check_is_undo_valid(&undo, &remove)?;
 
   let type_ = remove.object().as_single_kind_str().unwrap();
   match type_ {
@@ -92,6 +116,7 @@ async fn receive_undo_like(
   chat_server: ChatServerParam,
 ) -> Result<HttpResponse, LemmyError> {
   let like = Like::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
+  check_is_undo_valid(&undo, &like)?;
 
   let type_ = like.object().as_single_kind_str().unwrap();
   match type_ {
@@ -108,6 +133,9 @@ async fn receive_undo_dislike(
   _chat_server: ChatServerParam,
 ) -> Result<HttpResponse, LemmyError> {
   let dislike = Dislike::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
+  check_is_undo_valid(&undo, &dislike)?;
+
+  // TODO: need to implement Undo<Dislike>
 
   let type_ = dislike.object().as_single_kind_str().unwrap();
   Err(anyhow!("Undo Delete type {} not supported", type_).into())
@@ -123,7 +151,7 @@ async fn receive_undo_delete_comment(
   let user = get_user_from_activity(delete, client, pool).await?;
   let note = Note::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
 
-  let comment_ap_id = CommentForm::from_apub(&note, client, pool)
+  let comment_ap_id = CommentForm::from_apub(&note, client, pool, Some(user.actor_id()?))
     .await?
     .get_ap_id()?;
 
@@ -181,7 +209,7 @@ async fn receive_undo_remove_comment(
   let mod_ = get_user_from_activity(remove, client, pool).await?;
   let note = Note::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
 
-  let comment_ap_id = CommentForm::from_apub(&note, client, pool)
+  let comment_ap_id = CommentForm::from_apub(&note, client, pool, Some(mod_.actor_id()?))
     .await?
     .get_ap_id()?;
 
@@ -239,7 +267,7 @@ async fn receive_undo_delete_post(
   let user = get_user_from_activity(delete, client, pool).await?;
   let page = PageExt::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
 
-  let post_ap_id = PostForm::from_apub(&page, client, pool)
+  let post_ap_id = PostForm::from_apub(&page, client, pool, Some(user.actor_id()?))
     .await?
     .get_ap_id()?;
 
@@ -294,7 +322,7 @@ async fn receive_undo_remove_post(
   let mod_ = get_user_from_activity(remove, client, pool).await?;
   let page = PageExt::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
 
-  let post_ap_id = PostForm::from_apub(&page, client, pool)
+  let post_ap_id = PostForm::from_apub(&page, client, pool, Some(mod_.actor_id()?))
     .await?
     .get_ap_id()?;
 
@@ -349,7 +377,7 @@ async fn receive_undo_delete_community(
   let user = get_user_from_activity(delete, client, pool).await?;
   let group = GroupExt::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
 
-  let community_actor_id = CommunityForm::from_apub(&group, client, pool)
+  let community_actor_id = CommunityForm::from_apub(&group, client, pool, Some(user.actor_id()?))
     .await?
     .actor_id;
 
@@ -413,7 +441,7 @@ async fn receive_undo_remove_community(
   let mod_ = get_user_from_activity(remove, client, pool).await?;
   let group = GroupExt::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
 
-  let community_actor_id = CommunityForm::from_apub(&group, client, pool)
+  let community_actor_id = CommunityForm::from_apub(&group, client, pool, Some(mod_.actor_id()?))
     .await?
     .actor_id;
 
@@ -477,7 +505,7 @@ async fn receive_undo_like_comment(
   let user = get_user_from_activity(like, client, pool).await?;
   let note = Note::from_any_base(like.object().to_owned().one().unwrap())?.unwrap();
 
-  let comment = CommentForm::from_apub(&note, client, pool).await?;
+  let comment = CommentForm::from_apub(&note, client, pool, None).await?;
 
   let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, client, pool)
     .await?
@@ -523,7 +551,7 @@ async fn receive_undo_like_post(
   let user = get_user_from_activity(like, client, pool).await?;
   let page = PageExt::from_any_base(like.object().to_owned().one().unwrap())?.unwrap();
 
-  let post = PostForm::from_apub(&page, client, pool).await?;
+  let post = PostForm::from_apub(&page, client, pool, None).await?;
 
   let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, client, pool)
     .await?
index 9af1a2d38c75e18e9b02c3c3ae0561aec5def439..d669e5becfeedf3834a5987f076e9c1a64b0fe84 100644 (file)
@@ -10,6 +10,7 @@ use crate::{
       get_user_from_activity,
       receive_unhandled_activity,
     },
+    ActorType,
     FromApub,
     PageExt,
   },
@@ -24,6 +25,7 @@ use crate::{
 };
 use activitystreams::{activity::Update, base::AnyBase, object::Note, prelude::*};
 use actix_web::{client::Client, HttpResponse};
+use anyhow::anyhow;
 use lemmy_db::{
   comment::{Comment, CommentForm},
   comment_view::CommentView,
@@ -40,6 +42,11 @@ pub async fn receive_update(
   chat_server: ChatServerParam,
 ) -> Result<HttpResponse, LemmyError> {
   let update = Update::from_any_base(activity)?.unwrap();
+
+  // ensure that update and actor come from the same instance
+  let user = get_user_from_activity(&update, client, pool).await?;
+  update.id(user.actor_id()?.domain().unwrap())?;
+
   match update.object().as_single_kind_str() {
     Some("Page") => receive_update_post(update, client, pool, chat_server).await,
     Some("Note") => receive_update_comment(update, client, pool, chat_server).await,
@@ -56,16 +63,27 @@ async fn receive_update_post(
   let user = get_user_from_activity(&update, client, pool).await?;
   let page = PageExt::from_any_base(update.object().to_owned().one().unwrap())?.unwrap();
 
-  let post = PostForm::from_apub(&page, client, pool).await?;
+  let post = PostForm::from_apub(&page, client, pool, Some(user.actor_id()?)).await?;
+  if post.creator_id != user.id {
+    return Err(anyhow!("Actor for update activity and post creator need to be identical").into());
+  }
 
-  let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, client, pool)
-    .await?
-    .id;
+  let original_post = get_or_fetch_and_insert_post(&post.get_ap_id()?, client, pool).await?;
+  if post.ap_id != original_post.ap_id {
+    return Err(anyhow!("Updated post ID needs to be identical to the original ID").into());
+  }
 
-  blocking(pool, move |conn| Post::update(conn, post_id, &post)).await??;
+  let original_post_id = original_post.id;
+  blocking(pool, move |conn| {
+    Post::update(conn, original_post_id, &post)
+  })
+  .await??;
 
   // Refetch the view
-  let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
+  let post_view = blocking(pool, move |conn| {
+    PostView::read(conn, original_post_id, None)
+  })
+  .await??;
 
   let res = PostResponse { post: post_view };
 
@@ -88,14 +106,20 @@ async fn receive_update_comment(
   let note = Note::from_any_base(update.object().to_owned().one().unwrap())?.unwrap();
   let user = get_user_from_activity(&update, client, pool).await?;
 
-  let comment = CommentForm::from_apub(&note, client, pool).await?;
+  let comment = CommentForm::from_apub(&note, client, pool, Some(user.actor_id()?)).await?;
+  if comment.creator_id != user.id {
+    return Err(anyhow!("Actor for update activity and post creator need to be identical").into());
+  }
 
-  let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, client, pool)
-    .await?
-    .id;
+  let original_comment =
+    get_or_fetch_and_insert_comment(&comment.get_ap_id()?, client, pool).await?;
+  if comment.ap_id != original_comment.ap_id {
+    return Err(anyhow!("Updated post ID needs to be identical to the original ID").into());
+  }
 
+  let original_comment_id = original_comment.id;
   let updated_comment = blocking(pool, move |conn| {
-    Comment::update(conn, comment_id, &comment)
+    Comment::update(conn, original_comment_id, &comment)
   })
   .await??;
 
@@ -107,8 +131,10 @@ async fn receive_update_comment(
     send_local_notifs(mentions, updated_comment, &user, post, pool, false).await?;
 
   // Refetch the view
-  let comment_view =
-    blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
+  let comment_view = blocking(pool, move |conn| {
+    CommentView::read(conn, original_comment_id, None)
+  })
+  .await??;
 
   let res = CommentResponse {
     comment: comment_view,
index db44a99da42f7314af160a45e089ef7cb74a921c..5406d11195492915b0b1120e54bdecc47a6de680 100644 (file)
@@ -31,7 +31,7 @@ use activitystreams::{
   prelude::*,
 };
 use actix_web::{client::Client, web, HttpRequest, HttpResponse};
-use lemmy_db::user::User_;
+use lemmy_db::{community::Community, user::User_};
 use log::debug;
 use serde::{Deserialize, Serialize};
 use std::fmt::Debug;
@@ -71,12 +71,13 @@ pub async fn shared_inbox(
   // TODO: pass this actor in instead of using get_user_from_activity()
   let actor = get_or_fetch_and_upsert_actor(sender, &client, &pool).await?;
 
-  let community = get_community_id_from_activity(&activity).await;
+  // TODO: i dont think this works for Announce/Undo activities
+  //let community = get_community_id_from_activity(&activity).await;
 
   check_is_apub_id_valid(sender)?;
-  check_is_apub_id_valid(&community)?;
   verify(&request, actor.as_ref())?;
 
+  // TODO: probably better to do this after, so we dont store activities that fail a check somewhere
   insert_activity(actor.user_id(), activity.clone(), false, &pool).await?;
 
   let any_base = activity.clone().into_any_base()?;
@@ -116,13 +117,18 @@ where
   get_or_fetch_and_upsert_user(&user_uri, client, pool).await
 }
 
-pub(in crate::apub::inbox) async fn get_community_id_from_activity<T, A>(activity: &T) -> Url
+pub(in crate::apub::inbox) async fn get_community_from_activity<T, A>(
+  activity: &T,
+  client: &Client,
+  pool: &DbPool,
+) -> Result<Community, LemmyError>
 where
   T: AsBase<A> + ActorAndObjectRef + AsObject<A>,
 {
   let cc = activity.cc().unwrap();
   let cc = cc.as_many().unwrap();
-  cc.first().unwrap().as_xsd_any_uri().unwrap().to_owned()
+  let community_uri = cc.first().unwrap().as_xsd_any_uri().unwrap().to_owned();
+  get_or_fetch_and_upsert_community(&community_uri, client, pool).await
 }
 
 pub(in crate::apub::inbox) async fn announce_if_community_is_local<T, Kind>(
index 494fd9f5bb0ba1ada07260809e1c8fd24b077a29..53f1fad2c79ce51079fedc7c308a55e74ec1f442 100644 (file)
@@ -125,7 +125,8 @@ async fn receive_create_private_message(
   let create = Create::from_any_base(activity)?.unwrap();
   let note = Note::from_any_base(create.object().as_one().unwrap().to_owned())?.unwrap();
 
-  let private_message = PrivateMessageForm::from_apub(&note, client, pool).await?;
+  let domain = Some(create.id_unchecked().unwrap().to_owned());
+  let private_message = PrivateMessageForm::from_apub(&note, client, pool, domain).await?;
 
   let inserted_private_message = blocking(pool, move |conn| {
     PrivateMessage::create(conn, &private_message)
@@ -160,7 +161,8 @@ async fn receive_update_private_message(
   let update = Update::from_any_base(activity)?.unwrap();
   let note = Note::from_any_base(update.object().as_one().unwrap().to_owned())?.unwrap();
 
-  let private_message_form = PrivateMessageForm::from_apub(&note, client, pool).await?;
+  let domain = Some(update.id_unchecked().unwrap().to_owned());
+  let private_message_form = PrivateMessageForm::from_apub(&note, client, pool, domain).await?;
 
   let private_message_ap_id = private_message_form.ap_id.clone();
   let private_message = blocking(pool, move |conn| {
@@ -203,7 +205,8 @@ async fn receive_delete_private_message(
   let delete = Delete::from_any_base(activity)?.unwrap();
   let note = Note::from_any_base(delete.object().as_one().unwrap().to_owned())?.unwrap();
 
-  let private_message_form = PrivateMessageForm::from_apub(&note, client, pool).await?;
+  let domain = Some(delete.id_unchecked().unwrap().to_owned());
+  let private_message_form = PrivateMessageForm::from_apub(&note, client, pool, domain).await?;
 
   let private_message_ap_id = private_message_form.ap_id;
   let private_message = blocking(pool, move |conn| {
@@ -259,7 +262,8 @@ async fn receive_undo_delete_private_message(
   let delete = Delete::from_any_base(undo.object().as_one().unwrap().to_owned())?.unwrap();
   let note = Note::from_any_base(delete.object().as_one().unwrap().to_owned())?.unwrap();
 
-  let private_message = PrivateMessageForm::from_apub(&note, client, pool).await?;
+  let domain = Some(undo.id_unchecked().unwrap().to_owned());
+  let private_message = PrivateMessageForm::from_apub(&note, client, pool, domain).await?;
 
   let private_message_ap_id = private_message.ap_id.clone();
   let private_message_id = blocking(pool, move |conn| {
index 7f39afc7559ef7534a489de381d00a693f87e5e6..69272b49e27703696d05d897472bc4a047023bcf 100644 (file)
@@ -23,6 +23,8 @@ use crate::{
 use activitystreams::{
   activity::Follow,
   actor::{ApActor, Group, Person},
+  base::AsBase,
+  markers::Base,
   object::{Page, Tombstone},
   prelude::*,
 };
@@ -129,10 +131,19 @@ where
 #[async_trait::async_trait(?Send)]
 pub trait FromApub {
   type ApubType;
+  /// Converts an object from ActivityPub type to Lemmy internal type.
+  ///
+  /// * `apub` The object to read from
+  /// * `client` Web client to fetch remote actors with
+  /// * `pool` Database connection
+  /// * `expected_domain` If present, ensure that the apub object comes from the same domain as
+  ///                     this URL
+  ///
   async fn from_apub(
     apub: &Self::ApubType,
     client: &Client,
     pool: &DbPool,
+    expected_domain: Option<Url>,
   ) -> Result<Self, LemmyError>
   where
     Self: Sized;
@@ -178,6 +189,24 @@ pub trait ApubObjectType {
   ) -> Result<(), LemmyError>;
 }
 
+pub(in crate::apub) fn check_actor_domain<T, Kind>(
+  apub: &T,
+  expected_domain: Option<Url>,
+) -> Result<String, LemmyError>
+where
+  T: Base + AsBase<Kind>,
+{
+  let actor_id = if let Some(url) = expected_domain {
+    let domain = url.domain().unwrap();
+    apub.id(domain)?.unwrap()
+  } else {
+    let actor_id = apub.id_unchecked().unwrap();
+    check_is_apub_id_valid(&actor_id)?;
+    actor_id
+  };
+  Ok(actor_id.to_string())
+}
+
 #[async_trait::async_trait(?Send)]
 pub trait ApubLikeableType {
   async fn send_like(
index ed4dfe0f9a4d9df33fc1615e570783da5011a321..a2a83f4532a9f81a36f832ae696bca11236de316 100644 (file)
@@ -1,7 +1,8 @@
 use crate::{
+  api::{check_slurs, check_slurs_opt},
   apub::{
     activities::{generate_activity_id, send_activity_to_community},
-    check_is_apub_id_valid,
+    check_actor_domain,
     create_apub_response,
     create_apub_tombstone_response,
     create_tombstone,
@@ -155,6 +156,7 @@ impl FromApub for PostForm {
     page: &PageExt,
     client: &Client,
     pool: &DbPool,
+    expected_domain: Option<Url>,
   ) -> Result<PostForm, LemmyError> {
     let ext = &page.ext_one;
     let creator_actor_id = page
@@ -204,9 +206,14 @@ impl FromApub for PostForm {
       None => (None, None, None),
     };
 
-    let ap_id = page.inner.id_unchecked().unwrap().to_string();
-    check_is_apub_id_valid(&Url::parse(&ap_id)?)?;
-
+    let name = page
+      .inner
+      .summary()
+      .as_ref()
+      .unwrap()
+      .as_single_xsd_string()
+      .unwrap()
+      .to_string();
     let url = page
       .inner
       .url()
@@ -217,15 +224,10 @@ impl FromApub for PostForm {
       .content()
       .as_ref()
       .map(|c| c.as_single_xsd_string().unwrap().to_string());
+    check_slurs(&name)?;
+    check_slurs_opt(&body)?;
     Ok(PostForm {
-      name: page
-        .inner
-        .summary()
-        .as_ref()
-        .unwrap()
-        .as_single_xsd_string()
-        .unwrap()
-        .to_string(),
+      name,
       url,
       body,
       creator_id: creator.id,
@@ -249,7 +251,7 @@ impl FromApub for PostForm {
       embed_description,
       embed_html,
       thumbnail_url,
-      ap_id,
+      ap_id: check_actor_domain(page, expected_domain)?,
       local: false,
     })
   }
index af0f5610700b0d2625fc3c770aa2383f89c13539..23975c7c987f43039d4d206d9864dd8f4915d20c 100644 (file)
@@ -1,6 +1,7 @@
 use crate::{
   apub::{
     activities::{generate_activity_id, send_activity},
+    check_actor_domain,
     check_is_apub_id_valid,
     create_tombstone,
     fetcher::get_or_fetch_and_upsert_user,
@@ -76,6 +77,7 @@ impl FromApub for PrivateMessageForm {
     note: &Note,
     client: &Client,
     pool: &DbPool,
+    expected_domain: Option<Url>,
   ) -> Result<PrivateMessageForm, LemmyError> {
     let creator_actor_id = note
       .attributed_to()
@@ -103,7 +105,7 @@ impl FromApub for PrivateMessageForm {
       updated: note.updated().map(|u| u.to_owned().naive_local()),
       deleted: None,
       read: None,
-      ap_id,
+      ap_id: check_actor_domain(note, expected_domain)?,
       local: false,
     })
   }
index 80b91ddbbe06ea4f08934de761dc566dc2f55550..eb042da9d5fe0a6e2503b4c4411970a8020de0f3 100644 (file)
@@ -1,7 +1,8 @@
 use crate::{
+  api::{check_slurs, check_slurs_opt},
   apub::{
     activities::{generate_activity_id, send_activity},
-    check_is_apub_id_valid,
+    check_actor_domain,
     create_apub_response,
     insert_activity,
     ActorType,
@@ -206,7 +207,12 @@ impl ActorType for User_ {
 impl FromApub for UserForm {
   type ApubType = PersonExt;
   /// Parse an ActivityPub person received from another instance into a Lemmy user.
-  async fn from_apub(person: &PersonExt, _: &Client, _: &DbPool) -> Result<Self, LemmyError> {
+  async fn from_apub(
+    person: &PersonExt,
+    _: &Client,
+    _: &DbPool,
+    expected_domain: Option<Url>,
+  ) -> Result<Self, LemmyError> {
     let avatar = match person.icon() {
       Some(any_image) => Image::from_any_base(any_image.as_one().unwrap().clone())
         .unwrap()
@@ -218,21 +224,26 @@ impl FromApub for UserForm {
       None => None,
     };
 
-    // TODO: here and in community we could actually check against the exact domain where we fetched
-    //       the actor from, if we can pass it in somehow
-    let actor_id = person.id_unchecked().unwrap().to_string();
-    check_is_apub_id_valid(&Url::parse(&actor_id)?)?;
+    let name = person
+      .name()
+      .unwrap()
+      .one()
+      .unwrap()
+      .as_xsd_string()
+      .unwrap()
+      .to_string();
+    let preferred_username = person.inner.preferred_username().map(|u| u.to_string());
+    let bio = person
+      .inner
+      .summary()
+      .map(|s| s.as_single_xsd_string().unwrap().into());
+    check_slurs(&name)?;
+    check_slurs_opt(&preferred_username)?;
+    check_slurs_opt(&bio)?;
 
     Ok(UserForm {
-      name: person
-        .name()
-        .unwrap()
-        .one()
-        .unwrap()
-        .as_xsd_string()
-        .unwrap()
-        .to_string(),
-      preferred_username: person.inner.preferred_username().map(|u| u.to_string()),
+      name,
+      preferred_username,
       password_encrypted: "".to_string(),
       admin: false,
       banned: false,
@@ -247,11 +258,8 @@ impl FromApub for UserForm {
       show_avatars: false,
       send_notifications_to_email: false,
       matrix_user_id: None,
-      actor_id,
-      bio: person
-        .inner
-        .summary()
-        .map(|s| s.as_single_xsd_string().unwrap().into()),
+      actor_id: check_actor_domain(person, expected_domain)?,
+      bio,
       local: false,
       private_key: None,
       public_key: Some(person.ext_one.public_key.to_owned().public_key_pem),