]> Untitled Git - lemmy.git/commitdiff
Merge branch 'main' into federation-authorisation
authorFelix Ableitner <me@nutomic.com>
Fri, 7 Aug 2020 13:19:08 +0000 (15:19 +0200)
committerFelix Ableitner <me@nutomic.com>
Fri, 7 Aug 2020 13:19:08 +0000 (15:19 +0200)
1  2 
server/src/apub/community.rs
server/src/apub/inbox/activities/delete.rs
server/src/apub/inbox/activities/remove.rs
server/src/apub/inbox/activities/undo.rs
server/src/apub/user.rs

index 3773b8fb4d5ab59faeb47961fa84ddfb75e0e4ce,549d9349f6bf67881a20eca94ae143ecf915e3f2..8b522b447a6dcd6bd20e66a84c96f74fff27d0c9
@@@ -1,8 -1,6 +1,8 @@@
  use crate::{
 +  api::{check_slurs, check_slurs_opt},
    apub::{
      activities::{generate_activity_id, send_activity},
 +    check_actor_domain,
      create_apub_response,
      create_apub_tombstone_response,
      create_tombstone,
@@@ -34,7 -32,7 +34,7 @@@ use activitystreams::
    base::{AnyBase, BaseExt},
    collection::{OrderedCollection, UnorderedCollection},
    context,
-   object::Tombstone,
+   object::{Image, Tombstone},
    prelude::*,
    public,
  };
@@@ -324,12 -322,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()
        .unwrap();
  
      let creator = get_or_fetch_and_upsert_user(creator_uri, client, pool).await?;
 +    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)?;
  
+     let icon = match group.icon() {
+       Some(any_image) => Some(
+         Image::from_any_base(any_image.as_one().unwrap().clone())
+           .unwrap()
+           .unwrap()
+           .url()
+           .unwrap()
+           .as_single_xsd_any_uri()
+           .map(|u| u.to_string()),
+       ),
+       None => None,
+     };
+     let banner = match group.image() {
+       Some(any_image) => Some(
+         Image::from_any_base(any_image.as_one().unwrap().clone())
+           .unwrap()
+           .unwrap()
+           .url()
+           .unwrap()
+           .as_single_xsd_any_uri()
+           .map(|u| u.to_string()),
+       ),
+       None => None,
+     };
      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,
        updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
        deleted: None,
        nsfw: group.ext_one.sensitive,
 -      actor_id: group.inner.id_unchecked().unwrap().to_string(),
 +      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),
        last_refreshed_at: Some(naive_now()),
+       icon,
+       banner,
      })
    }
  }
@@@ -471,13 -486,12 +499,13 @@@ pub async fn do_announce
  
    insert_activity(community.creator_id, announce.clone(), true, pool).await?;
  
 -  // dont send to the instance where the activity originally came from, because that would result
 -  // in a database error (same data inserted twice)
    let mut to = community.get_follower_inboxes(pool).await?;
  
 +  // dont send to the local instance, nor to the instance where the activity originally came from,
 +  // because that would result in a database error (same data inserted twice)
    // this seems to be the "easiest" stable alternative for remove_item()
    to.retain(|x| *x != sender.get_shared_inbox_url());
 +  to.retain(|x| *x != community.get_shared_inbox_url());
  
    send_activity(client, &announce.into_any_base()?, community, to).await?;
  
index 223f9eb4e23927615bb59801c90fbe281fe45bef,627d15f6a792c4d1c3be53b3cb082c025d204c57..1a48931026c73aafe7a4fb248520ed199bbc3412
@@@ -7,7 -7,6 +7,7 @@@ use crate::
        get_user_from_activity,
        receive_unhandled_activity,
      },
 +    ActorType,
      FromApub,
      GroupExt,
      PageExt,
@@@ -58,7 -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()?;
  
@@@ -112,7 -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()?;
  
@@@ -169,7 -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;
  
      private_key: community.private_key,
      public_key: community.public_key,
      last_refreshed_at: None,
+     icon: Some(community.icon.to_owned()),
+     banner: Some(community.banner.to_owned()),
    };
  
    let community_id = community.id;
index 882954b6882db34c0fcf9d9c5183f3f1997b8875,91fca995d002f246d909d49bb903f7212e55ce42..f4869ae15929f5d0d56d40cb3d2fc29573ce3aea
@@@ -4,11 -4,9 +4,11 @@@ use crate::
      fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
      inbox::shared_inbox::{
        announce_if_community_is_local,
 +      get_community_id_from_activity,
        get_user_from_activity,
        receive_unhandled_activity,
      },
 +    ActorType,
      FromApub,
      GroupExt,
      PageExt,
@@@ -24,7 -22,6 +24,7 @@@
  };
  use activitystreams::{activity::Remove, base::AnyBase, object::Note, prelude::*};
  use actix_web::{client::Client, HttpResponse};
 +use anyhow::anyhow;
  use lemmy_db::{
    comment::{Comment, CommentForm},
    comment_view::CommentView,
@@@ -43,12 -40,6 +43,12 @@@ 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_id_from_activity(&remove)?;
 +  if actor.actor_id()?.domain() != community.domain() {
 +    return Err(anyhow!("Remove activities are only allowed on local objects").into());
 +  }
 +
    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,
@@@ -66,7 -57,7 +66,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()?;
  
@@@ -120,7 -111,7 +120,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()?;
  
@@@ -177,7 -168,7 +177,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;
  
      private_key: community.private_key,
      public_key: community.public_key,
      last_refreshed_at: None,
+     icon: Some(community.icon.to_owned()),
+     banner: Some(community.banner.to_owned()),
    };
  
    let community_id = community.id;
index b2c91597f7a2abd4acc7ecde8f7cf52f31e70007,87c78a03e3196c8a081e1eb9e1dfdad9ef684510..34e9e2109b15ebad44ca76438901df0c6a2efaf3
@@@ -7,7 -7,6 +7,7 @@@ use crate::
        get_user_from_activity,
        receive_unhandled_activity,
      },
 +    ActorType,
      FromApub,
      GroupExt,
      PageExt,
    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::{
@@@ -53,27 -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.domain() != inner_actor_uri.domain() {
 +    Err(anyhow!("Cant undo activities from a different instance").into())
 +  } else {
 +    Ok(())
 +  }
 +}
 +
  async fn receive_undo_delete(
    undo: Undo,
    client: &Client,
@@@ -81,7 -59,6 +81,7 @@@
    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,
@@@ -98,7 -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_ {
@@@ -116,7 -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_ {
@@@ -133,9 -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())
@@@ -151,7 -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()?;
  
@@@ -209,7 -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, None)
      .await?
      .get_ap_id()?;
  
@@@ -267,7 -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()?;
  
@@@ -322,7 -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, None)
      .await?
      .get_ap_id()?;
  
@@@ -377,7 -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;
  
      private_key: community.private_key,
      public_key: community.public_key,
      last_refreshed_at: None,
+     icon: Some(community.icon.to_owned()),
+     banner: Some(community.banner.to_owned()),
    };
  
    let community_id = community.id;
@@@ -441,7 -415,7 +443,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;
  
      private_key: community.private_key,
      public_key: community.public_key,
      last_refreshed_at: None,
+     icon: Some(community.icon.to_owned()),
+     banner: Some(community.banner.to_owned()),
    };
  
    let community_id = community.id;
@@@ -505,7 -481,7 +509,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?
@@@ -551,7 -527,7 +555,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?
diff --combined server/src/apub/user.rs
index eb042da9d5fe0a6e2503b4c4411970a8020de0f3,c20335fec8e0e03421e44672c90e9aaae3442f8b..58338ab4da3c93d77a25e6679fd9724af7fd9c39
@@@ -1,8 -1,6 +1,8 @@@
  use crate::{
 +  api::{check_slurs, check_slurs_opt},
    apub::{
      activities::{generate_activity_id, send_activity},
 +    check_actor_domain,
      create_apub_response,
      insert_activity,
      ActorType,
@@@ -65,6 -63,12 +65,12 @@@ impl ToApub for User_ 
        person.set_icon(image.into_any_base()?);
      }
  
+     if let Some(banner_url) = &self.banner {
+       let mut image = Image::new();
+       image.set_url(banner_url.to_owned());
+       person.set_image(image.into_any_base()?);
+     }
      if let Some(bio) = &self.bio {
        person.set_summary(bio.to_owned());
      }
@@@ -207,48 -211,49 +213,64 @@@ 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()
-         .unwrap()
-         .url()
-         .unwrap()
-         .as_single_xsd_any_uri()
-         .map(|u| u.to_string()),
+       Some(any_image) => Some(
+         Image::from_any_base(any_image.as_one().unwrap().clone())
+           .unwrap()
+           .unwrap()
+           .url()
+           .unwrap()
+           .as_single_xsd_any_uri()
+           .map(|u| u.to_string()),
+       ),
+       None => None,
+     };
+     let banner = match person.image() {
+       Some(any_image) => Some(
+         Image::from_any_base(any_image.as_one().unwrap().clone())
+           .unwrap()
+           .unwrap()
+           .url()
+           .unwrap()
+           .as_single_xsd_any_uri()
+           .map(|u| u.to_string()),
+       ),
        None => None,
      };
  
 +    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,
        email: None,
        avatar,
+       banner,
        updated: person.updated().map(|u| u.to_owned().naive_local()),
        show_nsfw: false,
        theme: "".to_string(),
        show_avatars: false,
        send_notifications_to_email: false,
        matrix_user_id: None,
 -      actor_id: person.id_unchecked().unwrap().to_string(),
 -      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),