Making a trait function for follow and accept.
This commit is contained in:
parent
c16fb9d00a
commit
9810e87eb2
11 changed files with 128 additions and 78 deletions
10
server/Cargo.lock
generated
vendored
10
server/Cargo.lock
generated
vendored
|
@ -1455,6 +1455,15 @@ dependencies = [
|
||||||
"sluice",
|
"sluice",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -1544,6 +1553,7 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"http-signature-normalization",
|
"http-signature-normalization",
|
||||||
"isahc",
|
"isahc",
|
||||||
|
"itertools",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lazy_static 1.4.0",
|
"lazy_static 1.4.0",
|
||||||
"lettre",
|
"lettre",
|
||||||
|
|
1
server/Cargo.toml
vendored
1
server/Cargo.toml
vendored
|
@ -44,3 +44,4 @@ http-signature-normalization = "0.4.1"
|
||||||
base64 = "0.12.0"
|
base64 = "0.12.0"
|
||||||
tokio = "0.2.18"
|
tokio = "0.2.18"
|
||||||
futures = "0.3.4"
|
futures = "0.3.4"
|
||||||
|
itertools = "0.9.0"
|
||||||
|
|
|
@ -488,7 +488,7 @@ impl Perform for Oper<FollowCommunity> {
|
||||||
} else {
|
} else {
|
||||||
// TODO: still have to implement unfollow
|
// TODO: still have to implement unfollow
|
||||||
let user = User_::read(&conn, user_id)?;
|
let user = User_::read(&conn, user_id)?;
|
||||||
follow_community(&community, &user, &conn)?;
|
user.send_follow(&community.actor_id)?;
|
||||||
// TODO: this needs to return a "pending" state, until Accept is received from the remote server
|
// TODO: this needs to return a "pending" state, until Accept is received from the remote server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,10 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::apub::{
|
use crate::apub::{
|
||||||
activities::{follow_community, post_create, post_update},
|
activities::{send_post_create, send_post_update},
|
||||||
fetcher::search_by_apub_id,
|
fetcher::search_by_apub_id,
|
||||||
signatures::generate_actor_keypair,
|
signatures::generate_actor_keypair,
|
||||||
{make_apub_endpoint, EndpointType},
|
{make_apub_endpoint, ActorType, EndpointType},
|
||||||
};
|
};
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::websocket::UserOperation;
|
use crate::websocket::UserOperation;
|
||||||
|
|
|
@ -160,7 +160,7 @@ impl Perform for Oper<CreatePost> {
|
||||||
Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
|
Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
post_create(&updated_post, &user, &conn)?;
|
send_post_create(&updated_post, &user, &conn)?;
|
||||||
|
|
||||||
// They like their own post by default
|
// They like their own post by default
|
||||||
let like_form = PostLikeForm {
|
let like_form = PostLikeForm {
|
||||||
|
@ -531,7 +531,7 @@ impl Perform for Oper<EditPost> {
|
||||||
ModStickyPost::create(&conn, &form)?;
|
ModStickyPost::create(&conn, &form)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
post_update(&updated_post, &user, &conn)?;
|
send_post_update(&updated_post, &user, &conn)?;
|
||||||
|
|
||||||
let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
|
let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn populate_object_props(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send an activity to a list of recipients, using the correct headers etc.
|
/// Send an activity to a list of recipients, using the correct headers etc.
|
||||||
fn send_activity<A>(
|
pub fn send_activity<A>(
|
||||||
activity: &A,
|
activity: &A,
|
||||||
private_key: &str,
|
private_key: &str,
|
||||||
sender_id: &str,
|
sender_id: &str,
|
||||||
|
@ -52,15 +52,18 @@ where
|
||||||
fn get_follower_inboxes(conn: &PgConnection, community: &Community) -> Result<Vec<String>, Error> {
|
fn get_follower_inboxes(conn: &PgConnection, community: &Community) -> Result<Vec<String>, Error> {
|
||||||
Ok(
|
Ok(
|
||||||
CommunityFollowerView::for_community(conn, community.id)?
|
CommunityFollowerView::for_community(conn, community.id)?
|
||||||
.iter()
|
.into_iter()
|
||||||
.filter(|c| !c.user_local)
|
.filter(|c| !c.user_local)
|
||||||
|
// TODO eventually this will have to use the inbox or shared_inbox column, meaning that view
|
||||||
|
// will have to change
|
||||||
.map(|c| format!("{}/inbox", c.user_actor_id.to_owned()))
|
.map(|c| format!("{}/inbox", c.user_actor_id.to_owned()))
|
||||||
|
.unique()
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send out information about a newly created post, to the followers of the community.
|
/// Send out information about a newly created post, to the followers of the community.
|
||||||
pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
pub fn send_post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
let page = post.to_apub(conn)?;
|
let page = post.to_apub(conn)?;
|
||||||
let community = Community::read(conn, post.community_id)?;
|
let community = Community::read(conn, post.community_id)?;
|
||||||
let mut create = Create::new();
|
let mut create = Create::new();
|
||||||
|
@ -83,7 +86,7 @@ pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send out information about an edited post, to the followers of the community.
|
/// Send out information about an edited post, to the followers of the community.
|
||||||
pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
pub fn send_post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
let page = post.to_apub(conn)?;
|
let page = post.to_apub(conn)?;
|
||||||
let community = Community::read(conn, post.community_id)?;
|
let community = Community::read(conn, post.community_id)?;
|
||||||
let mut update = Update::new();
|
let mut update = Update::new();
|
||||||
|
@ -104,70 +107,3 @@ pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// As a given local user, send out a follow request to a remote community.
|
|
||||||
pub fn follow_community(
|
|
||||||
community: &Community,
|
|
||||||
user: &User_,
|
|
||||||
_conn: &PgConnection,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let mut follow = Follow::new();
|
|
||||||
follow
|
|
||||||
.object_props
|
|
||||||
.set_context_xsd_any_uri(context())?
|
|
||||||
// TODO: needs proper id
|
|
||||||
.set_id(user.actor_id.clone())?;
|
|
||||||
follow
|
|
||||||
.follow_props
|
|
||||||
.set_actor_xsd_any_uri(user.actor_id.clone())?
|
|
||||||
.set_object_xsd_any_uri(community.actor_id.clone())?;
|
|
||||||
// TODO this is incorrect, the to field should not be the inbox, but the followers url
|
|
||||||
let to = format!("{}/inbox", community.actor_id);
|
|
||||||
send_activity(
|
|
||||||
&follow,
|
|
||||||
&user.private_key.as_ref().unwrap(),
|
|
||||||
&community.actor_id,
|
|
||||||
vec![to],
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// As a local community, accept the follow request from a remote user.
|
|
||||||
pub fn accept_follow(follow: &Follow, conn: &PgConnection) -> Result<(), Error> {
|
|
||||||
let community_uri = follow
|
|
||||||
.follow_props
|
|
||||||
.get_object_xsd_any_uri()
|
|
||||||
.unwrap()
|
|
||||||
.to_string();
|
|
||||||
let actor_uri = follow
|
|
||||||
.follow_props
|
|
||||||
.get_actor_xsd_any_uri()
|
|
||||||
.unwrap()
|
|
||||||
.to_string();
|
|
||||||
let community = Community::read_from_actor_id(conn, &community_uri)?;
|
|
||||||
let mut accept = Accept::new();
|
|
||||||
accept
|
|
||||||
.object_props
|
|
||||||
.set_context_xsd_any_uri(context())?
|
|
||||||
// TODO: needs proper id
|
|
||||||
.set_id(
|
|
||||||
follow
|
|
||||||
.follow_props
|
|
||||||
.get_actor_xsd_any_uri()
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
)?;
|
|
||||||
accept
|
|
||||||
.accept_props
|
|
||||||
.set_actor_xsd_any_uri(community.actor_id.clone())?
|
|
||||||
.set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
|
|
||||||
// TODO this is incorrect, the to field should not be the inbox, but the followers url
|
|
||||||
let to = format!("{}/inbox", actor_uri);
|
|
||||||
send_activity(
|
|
||||||
&accept,
|
|
||||||
&community.private_key.unwrap(),
|
|
||||||
&community.actor_id,
|
|
||||||
vec![to],
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,12 +30,17 @@ impl ToApub for Community {
|
||||||
oprops.set_summary_xsd_string(d)?;
|
oprops.set_summary_xsd_string(d)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut endpoint_props = EndpointProperties::default();
|
||||||
|
|
||||||
|
endpoint_props.set_shared_inbox(self.get_shared_inbox_url())?;
|
||||||
|
|
||||||
let mut actor_props = ApActorProperties::default();
|
let mut actor_props = ApActorProperties::default();
|
||||||
|
|
||||||
actor_props
|
actor_props
|
||||||
.set_preferred_username(self.title.to_owned())?
|
.set_preferred_username(self.title.to_owned())?
|
||||||
.set_inbox(self.get_inbox_url())?
|
.set_inbox(self.get_inbox_url())?
|
||||||
.set_outbox(self.get_outbox_url())?
|
.set_outbox(self.get_outbox_url())?
|
||||||
|
.set_endpoints(endpoint_props)?
|
||||||
.set_followers(self.get_followers_url())?;
|
.set_followers(self.get_followers_url())?;
|
||||||
|
|
||||||
Ok(group.extend(actor_props).extend(self.get_public_key_ext()))
|
Ok(group.extend(actor_props).extend(self.get_public_key_ext()))
|
||||||
|
@ -50,6 +55,40 @@ impl ActorType for Community {
|
||||||
fn public_key(&self) -> String {
|
fn public_key(&self) -> String {
|
||||||
self.public_key.to_owned().unwrap()
|
self.public_key.to_owned().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// As a local community, accept the follow request from a remote user.
|
||||||
|
fn send_accept_follow(&self, follow: &Follow) -> Result<(), Error> {
|
||||||
|
let actor_uri = follow
|
||||||
|
.follow_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let mut accept = Accept::new();
|
||||||
|
accept
|
||||||
|
.object_props
|
||||||
|
.set_context_xsd_any_uri(context())?
|
||||||
|
// TODO: needs proper id
|
||||||
|
.set_id(
|
||||||
|
follow
|
||||||
|
.follow_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
)?;
|
||||||
|
accept
|
||||||
|
.accept_props
|
||||||
|
.set_actor_xsd_any_uri(self.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
|
||||||
|
let to = format!("{}/inbox", actor_uri);
|
||||||
|
send_activity(
|
||||||
|
&accept,
|
||||||
|
&self.private_key.to_owned().unwrap(),
|
||||||
|
&self.actor_id,
|
||||||
|
vec![to],
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromApub for CommunityForm {
|
impl FromApub for CommunityForm {
|
||||||
|
|
|
@ -63,6 +63,7 @@ fn handle_follow(
|
||||||
// This will fail if they're already a follower, but ignore the error.
|
// This will fail if they're already a follower, but ignore the error.
|
||||||
CommunityFollower::follow(&conn, &community_follower_form).ok();
|
CommunityFollower::follow(&conn, &community_follower_form).ok();
|
||||||
|
|
||||||
accept_follow(&follow, &conn)?;
|
community.send_accept_follow(&follow)?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ pub mod community;
|
||||||
pub mod community_inbox;
|
pub mod community_inbox;
|
||||||
pub mod fetcher;
|
pub mod fetcher;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
|
pub mod shared_inbox;
|
||||||
pub mod signatures;
|
pub mod signatures;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod user_inbox;
|
pub mod user_inbox;
|
||||||
|
@ -12,6 +13,7 @@ use activitystreams::{
|
||||||
actor::{properties::ApActorProperties, Actor, Group, Person},
|
actor::{properties::ApActorProperties, Actor, Group, Person},
|
||||||
collection::UnorderedCollection,
|
collection::UnorderedCollection,
|
||||||
context,
|
context,
|
||||||
|
endpoint::EndpointProperties,
|
||||||
ext::{Ext, Extensible, Extension},
|
ext::{Ext, Extensible, Extension},
|
||||||
object::{properties::ObjectProperties, Page},
|
object::{properties::ObjectProperties, Page},
|
||||||
public, BaseBox,
|
public, BaseBox,
|
||||||
|
@ -26,6 +28,7 @@ use failure::_core::fmt::Debug;
|
||||||
use http::request::Builder;
|
use http::request::Builder;
|
||||||
use http_signature_normalization::Config;
|
use http_signature_normalization::Config;
|
||||||
use isahc::prelude::*;
|
use isahc::prelude::*;
|
||||||
|
use itertools::Itertools;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use openssl::hash::MessageDigest;
|
use openssl::hash::MessageDigest;
|
||||||
use openssl::sign::{Signer, Verifier};
|
use openssl::sign::{Signer, Verifier};
|
||||||
|
@ -47,7 +50,7 @@ use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
|
||||||
use crate::routes::{ChatServerParam, DbPoolParam};
|
use crate::routes::{ChatServerParam, DbPoolParam};
|
||||||
use crate::{convert_datetime, naive_now, Settings};
|
use crate::{convert_datetime, naive_now, Settings};
|
||||||
|
|
||||||
use activities::accept_follow;
|
use activities::send_activity;
|
||||||
use fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user};
|
use fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user};
|
||||||
use signatures::verify;
|
use signatures::verify;
|
||||||
use signatures::{sign, PublicKey, PublicKeyExtension};
|
use signatures::{sign, PublicKey, PublicKeyExtension};
|
||||||
|
@ -144,9 +147,38 @@ pub trait ActorType {
|
||||||
|
|
||||||
fn public_key(&self) -> String;
|
fn public_key(&self) -> String;
|
||||||
|
|
||||||
|
// These two have default impls, since currently a community can't follow anything,
|
||||||
|
// and a user can't be followed (yet)
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn send_follow(&self, follow_actor_id: &str) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn send_accept_follow(&self, follow: &Follow) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO move these to the db rows
|
||||||
fn get_inbox_url(&self) -> String {
|
fn get_inbox_url(&self) -> String {
|
||||||
format!("{}/inbox", &self.actor_id())
|
format!("{}/inbox", &self.actor_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_shared_inbox_url(&self) -> String {
|
||||||
|
let url = Url::parse(&self.actor_id()).unwrap();
|
||||||
|
let url_str = format!(
|
||||||
|
"{}://{}{}/inbox",
|
||||||
|
&url.scheme(),
|
||||||
|
&url.host_str().unwrap(),
|
||||||
|
if let Some(port) = url.port() {
|
||||||
|
format!(":{}", port)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
format!("{}/inbox", &url_str)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_outbox_url(&self) -> String {
|
fn get_outbox_url(&self) -> String {
|
||||||
format!("{}/outbox", &self.actor_id())
|
format!("{}/outbox", &self.actor_id())
|
||||||
}
|
}
|
||||||
|
@ -154,9 +186,11 @@ pub trait ActorType {
|
||||||
fn get_followers_url(&self) -> String {
|
fn get_followers_url(&self) -> String {
|
||||||
format!("{}/followers", &self.actor_id())
|
format!("{}/followers", &self.actor_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_following_url(&self) -> String {
|
fn get_following_url(&self) -> String {
|
||||||
format!("{}/following", &self.actor_id())
|
format!("{}/following", &self.actor_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_liked_url(&self) -> String {
|
fn get_liked_url(&self) -> String {
|
||||||
format!("{}/liked", &self.actor_id())
|
format!("{}/liked", &self.actor_id())
|
||||||
}
|
}
|
||||||
|
|
1
server/src/apub/shared_inbox.rs
Normal file
1
server/src/apub/shared_inbox.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// use super::*;
|
|
@ -27,11 +27,16 @@ impl ToApub for User_ {
|
||||||
oprops.set_name_xsd_string(i.to_owned())?;
|
oprops.set_name_xsd_string(i.to_owned())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut endpoint_props = EndpointProperties::default();
|
||||||
|
|
||||||
|
endpoint_props.set_shared_inbox(self.get_shared_inbox_url())?;
|
||||||
|
|
||||||
let mut actor_props = ApActorProperties::default();
|
let mut actor_props = ApActorProperties::default();
|
||||||
|
|
||||||
actor_props
|
actor_props
|
||||||
.set_inbox(self.get_inbox_url())?
|
.set_inbox(self.get_inbox_url())?
|
||||||
.set_outbox(self.get_outbox_url())?
|
.set_outbox(self.get_outbox_url())?
|
||||||
|
.set_endpoints(endpoint_props)?
|
||||||
.set_followers(self.get_followers_url())?
|
.set_followers(self.get_followers_url())?
|
||||||
.set_following(self.get_following_url())?
|
.set_following(self.get_following_url())?
|
||||||
.set_liked(self.get_liked_url())?;
|
.set_liked(self.get_liked_url())?;
|
||||||
|
@ -48,6 +53,29 @@ impl ActorType for User_ {
|
||||||
fn public_key(&self) -> String {
|
fn public_key(&self) -> String {
|
||||||
self.public_key.to_owned().unwrap()
|
self.public_key.to_owned().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO might be able to move this to a default trait fn
|
||||||
|
/// As a given local user, send out a follow request to a remote community.
|
||||||
|
fn send_follow(&self, follow_actor_id: &str) -> Result<(), Error> {
|
||||||
|
let mut follow = Follow::new();
|
||||||
|
follow
|
||||||
|
.object_props
|
||||||
|
.set_context_xsd_any_uri(context())?
|
||||||
|
// TODO: needs proper id
|
||||||
|
.set_id(self.actor_id.to_owned())?;
|
||||||
|
follow
|
||||||
|
.follow_props
|
||||||
|
.set_actor_xsd_any_uri(self.actor_id.to_owned())?
|
||||||
|
.set_object_xsd_any_uri(follow_actor_id)?;
|
||||||
|
let to = format!("{}/inbox", follow_actor_id);
|
||||||
|
send_activity(
|
||||||
|
&follow,
|
||||||
|
&self.private_key.as_ref().unwrap(),
|
||||||
|
&follow_actor_id,
|
||||||
|
vec![to],
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromApub for UserForm {
|
impl FromApub for UserForm {
|
||||||
|
|
Loading…
Reference in a new issue