From c37e11547061ee8cf93ec817a63b58a395108ff5 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Sun, 30 Jan 2022 00:22:01 +0900 Subject: [PATCH] Make User follow change of activitystreams --- plume-models/src/users.rs | 355 ++++++++++++++++++++++---------------- 1 file changed, 208 insertions(+), 147 deletions(-) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 1e27fb19..b02df023 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -4,12 +4,15 @@ use crate::{ safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, Result, UserEvent::*, CONFIG, ITEMS_PER_PAGE, USER_CHAN, }; -use activitypub::{ - activity::Delete, - actor::Person, +use activitystreams::{ + activity::{ActorAndObject, Create, Delete}, + actor::{ApActor, Endpoints, Person}, + base::{AnyBase, AsBase, Base}, collection::{OrderedCollection, OrderedCollectionPage}, - object::{Image, Tombstone}, - Activity, CustomObject, Endpoint, + iri, + object::{kind::ImageType, ApObject, Image, Tombstone}, + prelude::*, + primitives::OneOrMany, // CustomObject, }; use chrono::{NaiveDateTime, Utc}; use diesel::{self, BelongingToDsl, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; @@ -42,7 +45,7 @@ use std::{ use url::Url; use webfinger::*; -pub type CustomPerson = CustomObject; +// pub type CustomPerson = CustomObject; pub enum Role { Admin = 0, @@ -242,13 +245,16 @@ impl User { .ok_or(Error::Webfinger) } - fn fetch(url: &str) -> Result { + // fn fetch(url: &str) -> Result { + fn fetch(url: &str) -> Result { let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?; let text = &res.text()?; // without this workaround, publicKey is not correctly deserialized - let ap_sign = serde_json::from_str::(text)?; - let mut json = serde_json::from_str::(text)?; - json.custom_props = ap_sign; + // TODO: Implement + // let ap_sign = serde_json::from_str::(text)?; + // let mut json = serde_json::from_str::(text)?; + let json = serde_json::from_str::(text)?; + // json.custom_props = ap_sign; // TODO: implement Ok(json) } @@ -260,35 +266,69 @@ impl User { User::fetch(&self.ap_url.clone()).and_then(|json| { let avatar = Media::save_remote( conn, - json.object - .object_props - .icon_image()? // FIXME: Fails when icon is not set - .object_props - .url_string()?, + json.icon() + .and_then(|icon| { + Some( + icon.as_one() + .expect("only") + .extend::() + .expect("possible") + .expect("exists") + .url() + .ok_or(Error::MissingApProperty) + .ok()? + .as_one() + .expect("one") + .as_xsd_string() + .expect("possible"), + ) + }) + .ok_or(Error::MissingApProperty)? + .into(), // FIXME: Fails when icon is not set, self, ) .ok(); + let person = + serde_json::from_value::>(serde_json::to_value(json)?.into())?; + diesel::update(self) .set(( - users::username.eq(json.object.ap_actor_props.preferred_username_string()?), - users::display_name.eq(json.object.object_props.name_string()?), - users::outbox_url.eq(json.object.ap_actor_props.outbox_string()?), - users::inbox_url.eq(json.object.ap_actor_props.inbox_string()?), + users::username.eq(person + .preferred_username() + .ok_or(Error::MissingApProperty)?), + users::display_name.eq(person + .name() + .ok_or(Error::MissingApProperty)? + .as_one() + .expect("only") + .as_xsd_string() + .expect("possible")), + users::outbox_url.eq(person.outbox().ok_or(Error::MissingApProperty)?.as_str()), + users::inbox_url.eq(person.inbox().as_str()), users::summary.eq(SafeString::new( - &json - .object - .object_props - .summary_string() + &person + .summary() + .and_then(|summary| { + Some( + summary + .as_one() + .expect("only") + .as_xsd_string() + .expect("possible"), + ) + }) .unwrap_or_default(), )), - users::followers_endpoint.eq(json.object.ap_actor_props.followers_string()?), + users::followers_endpoint + .eq(person.followers().ok_or(Error::MissingApProperty)?.as_str()), users::avatar_id.eq(avatar.map(|a| a.id)), users::last_fetched_date.eq(Utc::now().naive_utc()), - users::public_key.eq(json - .custom_props - .public_key_publickey()? - .public_key_pem_string()?), + // TODO: + // users::public_key.eq(json + // .custom_props + // .public_key_publickey()? + // .public_key_pem_string()?), )) .execute(conn) .map(|_| ()) @@ -428,31 +468,32 @@ impl User { .load::(conn) .map_err(Error::from) } - pub fn outbox(&self, conn: &Connection) -> Result> { - Ok(ActivityStream::new(self.outbox_collection(conn)?)) + pub fn outbox(&self, conn: &Connection) -> Result>> { + Ok(ActivityStream::new(ApObject::new( + self.outbox_collection(conn)?, + ))) } pub fn outbox_collection(&self, conn: &Connection) -> Result { - let mut coll = OrderedCollection::default(); + let mut coll = OrderedCollection::new(); let first = &format!("{}?page=1", &self.outbox_url); let last = &format!( "{}?page={}", &self.outbox_url, self.get_activities_count(conn) / i64::from(ITEMS_PER_PAGE) + 1 ); - coll.collection_props.set_first_link(Id::new(first))?; - coll.collection_props.set_last_link(Id::new(last))?; - coll.collection_props - .set_total_items_u64(self.get_activities_count(conn) as u64)?; + coll.set_first(iri!(first)) + .set_last(iri!(last)) + .set_total_items(self.get_activities_count(conn) as u64); Ok(coll) } pub fn outbox_page( &self, conn: &Connection, (min, max): (i32, i32), - ) -> Result> { - Ok(ActivityStream::new( + ) -> Result>> { + Ok(ActivityStream::new(ApObject::new( self.outbox_collection_page(conn, (min, max))?, - )) + ))) } pub fn outbox_collection_page( &self, @@ -461,27 +502,33 @@ impl User { ) -> Result { let acts = self.get_activities_page(conn, (min, max))?; let n_acts = self.get_activities_count(conn); - let mut coll = OrderedCollectionPage::default(); + let mut coll = OrderedCollectionPage::new(); if n_acts - i64::from(min) >= i64::from(ITEMS_PER_PAGE) { - coll.collection_page_props.set_next_link(Id::new(&format!( + coll.set_next(iri!(&format!( "{}?page={}", &self.outbox_url, min / ITEMS_PER_PAGE + 2 - )))?; + ))); } if min > 0 { - coll.collection_page_props.set_prev_link(Id::new(&format!( + coll.set_prev(iri!(&format!( "{}?page={}", &self.outbox_url, min / ITEMS_PER_PAGE - )))?; + ))); } - coll.collection_props.items = serde_json::to_value(acts)?; - coll.collection_page_props - .set_part_of_link(Id::new(&self.outbox_url))?; + coll.set_many_items( + acts.iter().map(|create| { + AnyBase::from_base(create.base_ref().into_generic().expect("possible")) + }), + ) + .set_part_of(iri!(&self.outbox_url)); Ok(coll) } - fn fetch_outbox_page(&self, url: &str) -> Result<(Vec, Option)> { + fn fetch_outbox_page( + &self, + url: &str, + ) -> Result<(Vec>, Option)> { let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?; let text = &res.text()?; let json: serde_json::Value = serde_json::from_str(text)?; @@ -490,12 +537,12 @@ impl User { .unwrap_or(&vec![]) .iter() .filter_map(|j| serde_json::from_value(j.clone()).ok()) - .collect::>(); + .collect::>>(); let next = json.get("next").map(|x| x.as_str().unwrap().to_owned()); Ok((items, next)) } - pub fn fetch_outbox(&self) -> Result> { + pub fn fetch_outbox(&self) -> Result>> { let mut res = get( &self.outbox_url[..], Self::get_sender(), @@ -504,7 +551,7 @@ impl User { let text = &res.text()?; let json: serde_json::Value = serde_json::from_str(text)?; if let Some(first) = json.get("first") { - let mut items: Vec = Vec::new(); + let mut items: Vec> = Vec::new(); let mut next = first.as_str().unwrap().to_owned(); while let Ok((mut page, nxt)) = self.fetch_outbox_page(&next) { if page.is_empty() { @@ -527,7 +574,7 @@ impl User { .unwrap_or(&vec![]) .iter() .filter_map(|j| serde_json::from_value(j.clone()).ok()) - .collect::>()) + .collect::>>()) } } @@ -561,7 +608,7 @@ impl User { &self, conn: &Connection, (min, max): (i32, i32), - ) -> Result> { + ) -> Result> { use crate::schema::post_authors; use crate::schema::posts; let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id); @@ -574,12 +621,8 @@ impl User { .load::(conn)?; Ok(posts .into_iter() - .filter_map(|p| { - p.create_activity(conn) - .ok() - .and_then(|a| serde_json::to_value(a).ok()) - }) - .collect::>()) + .filter_map(|p| p.create_activity(conn).ok()) + .collect()) } pub fn get_followers(&self, conn: &Connection) -> Result> { @@ -739,72 +782,57 @@ impl User { } } - pub fn to_activity(&self, conn: &Connection) -> Result { - let mut actor = Person::default(); - actor.object_props.set_id_string(self.ap_url.clone())?; - actor - .object_props - .set_name_string(self.display_name.clone())?; - actor - .object_props - .set_summary_string(self.summary_html.get().clone())?; - actor.object_props.set_url_string(self.ap_url.clone())?; - actor - .ap_actor_props - .set_inbox_string(self.inbox_url.clone())?; - actor - .ap_actor_props - .set_outbox_string(self.outbox_url.clone())?; - actor - .ap_actor_props - .set_preferred_username_string(self.username.clone())?; - actor - .ap_actor_props - .set_followers_string(self.followers_endpoint.clone())?; + // pub fn to_activity(&self, conn: &Connection) -> Result { + pub fn to_activity(&self, conn: &Connection) -> Result> { + let mut actor = ApActor::new( + iri!(self.inbox_url), + *Person::new() + .set_name(self.display_name) + .set_summary(self.summary_html.get().to_owned()) + .set_url(self.ap_url), + ) + .set_outbox(iri!(self.outbox_url)) + .set_preferred_username(self.username) + .set_followers(iri!(self.followers_endpoint)); if let Some(shared_inbox_url) = self.shared_inbox_url.clone() { - let mut endpoints = Endpoint::default(); - endpoints.set_shared_inbox_string(shared_inbox_url)?; - actor.ap_actor_props.set_endpoints_endpoint(endpoints)?; + let endpoints = Endpoints { + shared_inbox: Some(iri!(shared_inbox_url)), + ..Endpoints::default() + }; + actor.set_endpoints(endpoints); } - let mut public_key = PublicKey::default(); - public_key.set_id_string(format!("{}#main-key", self.ap_url))?; - public_key.set_owner_string(self.ap_url.clone())?; - public_key.set_public_key_pem_string(self.public_key.clone())?; - let mut ap_signature = ApSignature::default(); - ap_signature.set_public_key_publickey(public_key)?; + // FIXME + // let mut public_key = PublicKey::default(); + // public_key.set_id_string(format!("{}#main-key", self.ap_url))?; + // public_key.set_owner_string(self.ap_url.clone())?; + // public_key.set_public_key_pem_string(self.public_key.clone())?; + // let mut ap_signature = ApSignature::default(); + // ap_signature.set_public_key_publickey(public_key)?; if let Some(avatar_id) = self.avatar_id { - let mut avatar = Image::default(); - avatar - .object_props - .set_url_string(Media::get(conn, avatar_id)?.url()?)?; - actor.object_props.set_icon_object(avatar)?; + let avatar = Image::new().set_url(iri!(Media::get(conn, avatar_id)?.url()?)); + let base = Base::retract(*avatar)?.into_generic()?; + actor.set_icon(AnyBase::from_base(base)); } - Ok(CustomPerson::new(actor, ap_signature)) + // Ok(CustomPerson::new(actor, ap_signature)) + Ok(*actor) } pub fn delete_activity(&self, conn: &Connection) -> Result { - let mut del = Delete::default(); - - let mut tombstone = Tombstone::default(); - tombstone.object_props.set_id_string(self.ap_url.clone())?; - - del.delete_props - .set_actor_link(Id::new(self.ap_url.clone()))?; - del.delete_props.set_object_object(tombstone)?; - del.object_props - .set_id_string(format!("{}#delete", self.ap_url))?; - del.object_props - .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY)])?; - del.object_props.set_cc_link_vec( - self.get_followers(conn)? - .into_iter() - .map(|f| Id::new(f.ap_url)) - .collect(), - )?; + let followers = self.get_followers(conn)?; + let ccs = Vec::with_capacity(followers.len()); + for addr in followers.into_iter() { + ccs.push(iri!(addr.ap_url)); + } + let tombstone = Tombstone::new() + .set_id(iri!(format!("{}#delete", self.ap_url))) + .set_many_tos([iri!(PUBLIC_VISIBILITY)]) + .set_many_ccs(ccs); + let base = Base::retract(*tombstone)?.into_generic()?; + let del = Delete::new::<_, OneOrMany>(iri!(self.ap_url), base.into()); Ok(del) } @@ -923,14 +951,16 @@ impl Eq for User {} impl FromId for User { type Error = Error; - type Object = CustomPerson; + // type Object = CustomPerson; + type Object = Person; fn from_db(conn: &DbConn, id: &str) -> Result { Self::find_by_ap_url(conn, id) } - fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result { - let url = Url::parse(&acct.object.object_props.id_string()?)?; + // fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result { + fn from_activity(conn: &DbConn, acct: Person) -> Result { + let url = Url::parse(&acct.id().ok_or(Error::MissingApProperty)?.as_str())?; let inst = url.host_str().ok_or(Error::Url)?; let instance = Instance::find_by_domain(conn, inst).or_else(|_| { Instance::insert( @@ -949,8 +979,12 @@ impl FromId for User { }, ) })?; + let person = serde_json::from_value::>(serde_json::to_value(acct)?.into())?; - let username = acct.object.ap_actor_props.preferred_username_string()?; + let username = person + .preferred_username() + .ok_or(Error::MissingApProperty)? + .to_string(); if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) { return Err(Error::InvalidValue); @@ -966,50 +1000,77 @@ impl FromId for User { conn, NewUser { display_name: acct - .object - .object_props - .name_string() - .unwrap_or_else(|_| username.clone()), + .name() + .map(|name| { + name.as_one() + .expect("only") + .as_xsd_string() + .expect("exists") + }) + .unwrap_or_else(|| &username) + .to_string(), username, - outbox_url: acct.object.ap_actor_props.outbox_string()?, - inbox_url: acct.object.ap_actor_props.inbox_string()?, + outbox_url: person.outbox().ok_or(Error::MissingApProperty)?.to_string(), + inbox_url: person.inbox().to_string(), role: 2, - summary: acct - .object - .object_props - .summary_string() - .unwrap_or_default(), + summary: person + .summary() + .map(|summary| { + summary + .as_one() + .expect("only") + .as_xsd_string() + .expect("exists") + }) + .unwrap_or_default() + .to_string(), summary_html: SafeString::new( - &acct - .object - .object_props - .summary_string() + &person + .summary() + .map(|summary| { + summary + .as_one() + .expect("only") + .as_xsd_string() + .expect("exists") + }) .unwrap_or_default(), ), email: None, hashed_password: None, instance_id: instance.id, - ap_url: acct.object.object_props.id_string()?, - public_key: acct - .custom_props - .public_key_publickey()? - .public_key_pem_string()?, + ap_url: person.id().ok_or(Error::MissingApProperty)?.to_string(), + // public_key: acct + // .custom_props + // .public_key_publickey()? + // .public_key_pem_string()?, + public_key: "".to_string(), // FIXME private_key: None, - shared_inbox_url: acct - .object - .ap_actor_props - .endpoints_endpoint() - .and_then(|e| e.shared_inbox_string()) - .ok(), - followers_endpoint: acct.object.ap_actor_props.followers_string()?, + shared_inbox_url: person + .endpoints() + .and_then(|e| e.shared_inbox.map(|shared_inbox| shared_inbox.to_string())), + followers_endpoint: person + .followers() + .ok_or(Error::MissingApProperty)? + .to_string(), fqn, avatar_id: None, }, )?; - if let Ok(icon) = acct.object.object_props.icon_image() { - if let Ok(url) = icon.object_props.url_string() { - let avatar = Media::save_remote(conn, url, &user); + if let Some(icon) = acct.icon() { + let icon_image = icon + .as_one() + .expect("only") + .extend::() + .expect("possible") + .expect("exists"); + if let Some(url) = icon_image.url() { + let avatar = Media::save_remote( + conn, + url.as_single_xsd_string().expect("exists").into(), + &user, + ); if let Ok(avatar) = avatar { user.set_avatar(conn, avatar.id)?;