]> Untitled Git - lemmy.git/blobdiff - server/src/apub/user.rs
Merge branch 'main' into federation-authorisation
[lemmy.git] / server / src / apub / user.rs
index a3194355ae29bb75c0e1d24b3c253b6586d19476..58338ab4da3c93d77a25e6679fd9724af7fd9c39 100644 (file)
@@ -1,28 +1,40 @@
 use crate::{
-  apub::{activities::send_activity, create_apub_response, ActorType, FromApub, PersonExt, ToApub},
-  blocking,
-  convert_datetime,
-  db::{
-    activity::insert_activity,
-    user::{UserForm, User_},
+  api::{check_slurs, check_slurs_opt},
+  apub::{
+    activities::{generate_activity_id, send_activity},
+    check_actor_domain,
+    create_apub_response,
+    insert_activity,
+    ActorType,
+    FromApub,
+    PersonExt,
+    ToApub,
   },
-  naive_now,
+  blocking,
   routes::DbPoolParam,
   DbPool,
   LemmyError,
 };
-use activitystreams_ext::Ext1;
-use activitystreams_new::{
-  activity::{Follow, Undo},
+use activitystreams::{
+  activity::{
+    kind::{FollowType, UndoType},
+    Follow,
+    Undo,
+  },
   actor::{ApActor, Endpoints, Person},
   context,
   object::{Image, Tombstone},
   prelude::*,
-  primitives::{XsdAnyUri, XsdDateTime},
 };
+use activitystreams_ext::Ext1;
 use actix_web::{body::Body, client::Client, web, HttpResponse};
-use failure::_core::str::FromStr;
+use lemmy_db::{
+  naive_now,
+  user::{UserForm, User_},
+};
+use lemmy_utils::convert_datetime;
 use serde::Deserialize;
+use url::Url;
 
 #[derive(Deserialize)]
 pub struct UserQuery {
@@ -39,12 +51,12 @@ impl ToApub for User_ {
     let mut person = Person::new();
     person
       .set_context(context())
-      .set_id(XsdAnyUri::from_str(&self.actor_id)?)
+      .set_id(Url::parse(&self.actor_id)?)
       .set_name(self.name.to_owned())
-      .set_published(XsdDateTime::from(convert_datetime(self.published)));
+      .set_published(convert_datetime(self.published));
 
     if let Some(u) = self.updated {
-      person.set_updated(XsdDateTime::from(convert_datetime(u)));
+      person.set_updated(convert_datetime(u));
     }
 
     if let Some(avatar_url) = &self.avatar {
@@ -53,9 +65,19 @@ impl ToApub for User_ {
       person.set_icon(image.into_any_base()?);
     }
 
-    let mut ap_actor = ApActor::new(self.get_inbox_url().parse()?, person);
+    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());
+    }
+
+    let mut ap_actor = ApActor::new(self.get_inbox_url()?, person);
     ap_actor
-      .set_outbox(self.get_outbox_url().parse()?)
+      .set_outbox(self.get_outbox_url()?)
       .set_followers(self.get_followers_url().parse()?)
       .set_following(self.get_following_url().parse()?)
       .set_liked(self.get_liked_url().parse()?)
@@ -77,7 +99,7 @@ impl ToApub for User_ {
 
 #[async_trait::async_trait(?Send)]
 impl ActorType for User_ {
-  fn actor_id(&self) -> String {
+  fn actor_id_str(&self) -> String {
     self.actor_id.to_owned()
   }
 
@@ -96,14 +118,15 @@ impl ActorType for User_ {
     client: &Client,
     pool: &DbPool,
   ) -> Result<(), LemmyError> {
-    let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
-    follow.set_context(context()).set_id(id.parse()?);
+    follow
+      .set_context(context())
+      .set_id(generate_activity_id(FollowType::Follow)?);
     let to = format!("{}/inbox", follow_actor_id);
 
     insert_activity(self.id, follow.clone(), true, pool).await?;
 
-    send_activity(client, &follow, self, vec![to]).await?;
+    send_activity(client, &follow.into_any_base()?, self, vec![to]).await?;
     Ok(())
   }
 
@@ -113,21 +136,22 @@ impl ActorType for User_ {
     client: &Client,
     pool: &DbPool,
   ) -> Result<(), LemmyError> {
-    let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
-    follow.set_context(context()).set_id(id.parse()?);
+    follow
+      .set_context(context())
+      .set_id(generate_activity_id(FollowType::Follow)?);
 
     let to = format!("{}/inbox", follow_actor_id);
 
-    // TODO
     // Undo that fake activity
-    let undo_id = format!("{}/undo/follow/{}", self.actor_id, uuid::Uuid::new_v4());
-    let mut undo = Undo::new(self.actor_id.parse::<XsdAnyUri>()?, follow.into_any_base()?);
-    undo.set_context(context()).set_id(undo_id.parse()?);
+    let mut undo = Undo::new(Url::parse(&self.actor_id)?, follow.into_any_base()?);
+    undo
+      .set_context(context())
+      .set_id(generate_activity_id(UndoType::Undo)?);
 
     insert_activity(self.id, undo.clone(), true, pool).await?;
 
-    send_activity(client, &undo, self, vec![to]).await?;
+    send_activity(client, &undo.into_any_base()?, self, vec![to]).await?;
     Ok(())
   }
 
@@ -169,7 +193,7 @@ impl ActorType for User_ {
 
   async fn send_accept_follow(
     &self,
-    _follow: &Follow,
+    _follow: Follow,
     _client: &Client,
     _pool: &DbPool,
   ) -> Result<(), LemmyError> {
@@ -179,40 +203,75 @@ impl ActorType for User_ {
   async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result<Vec<String>, LemmyError> {
     unimplemented!()
   }
+
+  fn user_id(&self) -> i32 {
+    self.id
+  }
 }
 
 #[async_trait::async_trait(?Send)]
 impl FromApub for UserForm {
   type ApubType = PersonExt;
   /// Parse an ActivityPub person received from another instance into a Lemmy user.
-  async fn from_apub(person: &mut PersonExt, _: &Client, _: &DbPool) -> Result<Self, LemmyError> {
-    let avatar = match person.take_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()),
+  async fn from_apub(
+    person: &PersonExt,
+    _: &Client,
+    _: &DbPool,
+    expected_domain: Option<Url>,
+  ) -> Result<Self, LemmyError> {
+    let avatar = match person.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 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
-        .take_name()
-        .unwrap()
-        .as_single_xsd_string()
-        .unwrap()
-        .into(),
-      preferred_username: person.inner.take_preferred_username(),
+      name,
+      preferred_username,
       password_encrypted: "".to_string(),
       admin: false,
       banned: false,
       email: None,
       avatar,
-      updated: person
-        .take_updated()
-        .map(|u| u.as_ref().to_owned().naive_local()),
+      banner,
+      updated: person.updated().map(|u| u.to_owned().naive_local()),
       show_nsfw: false,
       theme: "".to_string(),
       default_sort_type: 0,
@@ -221,10 +280,8 @@ impl FromApub for UserForm {
       show_avatars: false,
       send_notifications_to_email: false,
       matrix_user_id: None,
-      actor_id: person.id().unwrap().to_string(),
-      bio: person
-        .take_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),