Make User follow change of activitystreams

This commit is contained in:
Kitaiti Makoto 2022-01-30 00:22:01 +09:00
parent 00d31e323f
commit c37e115470

View File

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