lemmy/server/src/apub/community_inbox.rs

193 lines
5.4 KiB
Rust
Raw Normal View History

2020-05-16 14:04:08 +00:00
use crate::{
apub::{
2020-05-28 13:20:12 +00:00
activities::{populate_object_props, send_activity},
2020-05-16 14:04:08 +00:00
extensions::signatures::verify,
fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user},
ActorType,
},
db::{
activity::insert_activity,
community::{Community, CommunityFollower, CommunityFollowerForm},
user::User_,
Followable,
},
routes::{ChatServerParam, DbPoolParam},
};
2020-05-28 13:20:12 +00:00
use activitystreams::{
activity::{Activity, Announce, Create, Delete, Follow, Remove, Undo, Update},
Base,
BaseBox,
};
2020-05-16 14:04:08 +00:00
use actix_web::{web, HttpRequest, HttpResponse, Result};
use diesel::PgConnection;
use failure::{Error, _core::fmt::Debug};
use log::debug;
2020-05-06 17:10:36 +00:00
use serde::{Deserialize, Serialize};
#[serde(untagged)]
2020-04-17 14:55:28 +00:00
#[derive(Deserialize, Debug)]
pub enum CommunityAcceptedObjects {
Follow(Follow),
2020-05-04 02:41:45 +00:00
Undo(Undo),
2020-05-06 17:10:36 +00:00
Create(Create),
Update(Update),
Delete(Delete),
Remove(Remove),
}
2020-05-14 15:44:01 +00:00
impl CommunityAcceptedObjects {
fn follow(&self) -> Result<Follow, Error> {
match self {
CommunityAcceptedObjects::Follow(f) => Ok(f.to_owned()),
CommunityAcceptedObjects::Undo(u) => Ok(
u.undo_props
.get_object_base_box()
.to_owned()
.unwrap()
.to_owned()
.into_concrete::<Follow>()?,
),
2020-05-28 13:20:12 +00:00
_ => todo!(),
2020-05-14 15:44:01 +00:00
}
}
}
2020-04-17 15:33:55 +00:00
/// Handler for all incoming activities to community inboxes.
pub async fn community_inbox(
2020-04-19 17:35:40 +00:00
request: HttpRequest,
input: web::Json<CommunityAcceptedObjects>,
2020-04-17 17:34:18 +00:00
path: web::Path<String>,
2020-04-24 14:04:36 +00:00
db: DbPoolParam,
2020-05-06 17:10:36 +00:00
chat_server: ChatServerParam,
) -> Result<HttpResponse, Error> {
let input = input.into_inner();
2020-05-06 17:10:36 +00:00
let conn = db.get()?;
let community = Community::read_from_name(&conn, &path.into_inner())?;
if !community.local {
return Err(format_err!(
"Received activity is addressed to remote community {}",
&community.actor_id
));
}
2020-04-17 14:55:28 +00:00
debug!(
"Community {} received activity {:?}",
2020-05-06 17:10:36 +00:00
&community.name, &input
2020-04-17 14:55:28 +00:00
);
2020-05-14 15:44:01 +00:00
let follow = input.follow()?;
2020-04-19 17:35:40 +00:00
let user_uri = follow
.follow_props
.get_actor_xsd_any_uri()
.unwrap()
.to_string();
2020-05-14 15:44:01 +00:00
let community_uri = follow
.follow_props
.get_object_xsd_any_uri()
.unwrap()
.to_string();
2020-04-24 14:04:36 +00:00
let conn = db.get()?;
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
2020-05-14 15:44:01 +00:00
let community = get_or_fetch_and_upsert_remote_community(&community_uri, &conn)?;
2020-04-24 14:04:36 +00:00
verify(&request, &user)?;
2020-04-24 14:04:36 +00:00
2020-05-14 15:44:01 +00:00
match input {
2020-05-28 13:20:12 +00:00
CommunityAcceptedObjects::Follow(f) => handle_follow(&f, &user, &community, &conn),
2020-05-06 17:10:36 +00:00
CommunityAcceptedObjects::Undo(u) => {
// TODO: if this is an undo<remove> or undo<delete>, we need to announce it instead
2020-05-28 13:20:12 +00:00
handle_undo_follow(&u, &user, &community, &conn)
2020-05-06 17:10:36 +00:00
}
// TODO: we should be able to handle all this with a single wildcard match, but i dont see how
// to get the value from that
2020-05-28 13:20:12 +00:00
CommunityAcceptedObjects::Create(c) => do_announce(c, &request, &community, &conn, chat_server),
CommunityAcceptedObjects::Update(u) => do_announce(u, &request, &community, &conn, chat_server),
CommunityAcceptedObjects::Delete(d) => do_announce(d, &request, &community, &conn, chat_server),
CommunityAcceptedObjects::Remove(r) => do_announce(r, &request, &community, &conn, chat_server),
2020-05-14 15:44:01 +00:00
}
}
/// Handle a follow request from a remote user, adding it to the local database and returning an
/// Accept activity.
fn handle_follow(
follow: &Follow,
user: &User_,
community: &Community,
conn: &PgConnection,
) -> Result<HttpResponse, Error> {
insert_activity(&conn, user.id, &follow, false)?;
2020-04-27 22:17:02 +00:00
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
user_id: user.id,
};
2020-04-24 14:04:36 +00:00
// This will fail if they're already a follower, but ignore the error.
CommunityFollower::follow(&conn, &community_follower_form).ok();
2020-04-24 14:04:36 +00:00
2020-04-27 22:17:02 +00:00
community.send_accept_follow(&follow, &conn)?;
Ok(HttpResponse::Ok().finish())
}
2020-05-04 02:41:45 +00:00
fn handle_undo_follow(
undo: &Undo,
2020-05-14 15:44:01 +00:00
user: &User_,
community: &Community,
conn: &PgConnection,
2020-05-04 02:41:45 +00:00
) -> Result<HttpResponse, Error> {
2020-05-14 15:44:01 +00:00
insert_activity(&conn, user.id, &undo, false)?;
2020-05-04 02:41:45 +00:00
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
user_id: user.id,
};
CommunityFollower::unfollow(&conn, &community_follower_form).ok();
2020-05-04 02:41:45 +00:00
Ok(HttpResponse::Ok().finish())
}
2020-05-06 17:10:36 +00:00
fn do_announce<A>(
activity: A,
_request: &HttpRequest,
community: &Community,
conn: &PgConnection,
_chat_server: ChatServerParam,
) -> Result<HttpResponse, Error>
where
A: Activity + Base + Serialize,
{
// TODO: checking the signature needs a lot of boilerplate, unless this gets implemented
// https://git.asonix.dog/Aardwolf/activitystreams/issues/4
/*
let user_uri = activity
.follow_props
.get_actor_xsd_any_uri()
.unwrap()
.to_string();
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
verify(&request, &user.public_key.unwrap())?;
*/
insert_activity(&conn, -1, &activity, false)?;
// TODO: handle the sending in community.rs
let mut announce = Announce::default();
populate_object_props(
&mut announce.object_props,
2020-05-28 13:20:12 +00:00
vec![community.get_followers_url()],
2020-05-06 17:10:36 +00:00
&format!("{}/announce/{}", community.actor_id, uuid::Uuid::new_v4()),
)?;
announce
.announce_props
.set_actor_xsd_any_uri(community.actor_id.to_owned())?
.set_object_base_box(BaseBox::from_concrete(activity)?)?;
insert_activity(&conn, -1, &announce, true)?;
2020-05-28 13:20:12 +00:00
send_activity(&announce, community, community.get_follower_inboxes(&conn)?)?;
2020-05-06 17:10:36 +00:00
Ok(HttpResponse::Ok().finish())
}