lemmy/server/src/apub/mod.rs

159 lines
4.4 KiB
Rust
Raw Normal View History

2020-04-09 19:04:31 +00:00
pub mod activities;
pub mod community;
pub mod community_inbox;
pub mod fetcher;
pub mod post;
2020-04-10 13:50:40 +00:00
pub mod signatures;
pub mod user;
pub mod user_inbox;
2020-04-24 14:04:36 +00:00
use activitystreams::{
activity::{Accept, Create, Follow, Update},
2020-04-24 19:55:54 +00:00
actor::{properties::ApActorProperties, Actor, Group, Person},
collection::UnorderedCollection,
context,
ext::{Ext, Extensible, Extension},
object::{properties::ObjectProperties, Page},
public, BaseBox,
2020-04-24 14:04:36 +00:00
};
2020-03-16 17:30:25 +00:00
use actix_web::body::Body;
2020-04-24 14:04:36 +00:00
use actix_web::web::Path;
2020-04-24 19:55:54 +00:00
use actix_web::{web, HttpRequest, HttpResponse, Result};
2020-04-24 14:04:36 +00:00
use diesel::result::Error::NotFound;
use diesel::PgConnection;
2020-04-24 19:55:54 +00:00
use failure::Error;
use failure::_core::fmt::Debug;
2020-04-24 14:04:36 +00:00
use http::request::Builder;
use http_signature_normalization::Config;
2020-04-24 19:55:54 +00:00
use isahc::prelude::*;
use log::debug;
2020-04-24 14:04:36 +00:00
use openssl::hash::MessageDigest;
use openssl::sign::{Signer, Verifier};
use openssl::{pkey::PKey, rsa::Rsa};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::time::Duration;
2020-04-24 19:55:54 +00:00
use url::Url;
2020-04-24 14:04:36 +00:00
2020-04-24 19:55:54 +00:00
use crate::api::site::SearchResponse;
use crate::db::community::{Community, CommunityFollower, CommunityFollowerForm, CommunityForm};
2020-04-24 14:04:36 +00:00
use crate::db::community_view::{CommunityFollowerView, CommunityView};
use crate::db::post::{Post, PostForm};
use crate::db::user::{UserForm, User_};
use crate::db::user_view::UserView;
2020-04-24 19:55:54 +00:00
use crate::db::{Crud, Followable, SearchType};
use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
use crate::routes::{ChatServerParam, DbPoolParam};
use crate::{convert_datetime, naive_now, Settings};
2020-04-24 14:04:36 +00:00
use activities::accept_follow;
2020-04-24 19:55:54 +00:00
use fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user};
2020-04-24 14:04:36 +00:00
use signatures::verify;
2020-04-24 19:55:54 +00:00
use signatures::{sign, PublicKey, PublicKeyExtension};
2020-04-10 13:50:40 +00:00
type GroupExt = Ext<Ext<Group, ApActorProperties>, PublicKeyExtension>;
type PersonExt = Ext<Ext<Person, ApActorProperties>, PublicKeyExtension>;
pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
pub enum EndpointType {
Community,
User,
Post,
Comment,
}
2020-04-17 15:33:55 +00:00
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
/// headers.
fn create_apub_response<T>(data: &T) -> HttpResponse<Body>
2020-03-19 01:16:17 +00:00
where
2020-04-17 15:33:55 +00:00
T: Serialize,
2020-03-19 01:16:17 +00:00
{
2020-03-16 17:30:25 +00:00
HttpResponse::Ok()
.content_type(APUB_JSON_CONTENT_TYPE)
2020-04-17 15:33:55 +00:00
.json(data)
}
2020-04-17 15:33:55 +00:00
/// Generates the ActivityPub ID for a given object type and name.
///
/// TODO: we will probably need to change apub endpoint urls so that html and activity+json content
/// types are handled at the same endpoint, so that you can copy the url into mastodon search
/// and have it fetch the object.
pub fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url {
let point = match endpoint_type {
2020-04-07 15:34:44 +00:00
EndpointType::Community => "c",
EndpointType::User => "u",
EndpointType::Post => "post",
// TODO I have to change this else my update advanced_migrations crashes the
// server if a comment exists.
EndpointType::Comment => "comment",
};
Url::parse(&format!(
"{}://{}/{}/{}",
2020-02-29 17:38:47 +00:00
get_apub_protocol_string(),
Settings::get().hostname,
point,
name
))
.unwrap()
}
2020-02-29 17:38:47 +00:00
pub fn get_apub_protocol_string() -> &'static str {
2020-03-18 21:09:00 +00:00
if Settings::get().federation.tls_enabled {
"https"
} else {
"http"
}
2020-02-29 17:38:47 +00:00
}
2020-04-17 17:34:18 +00:00
// Checks if the ID has a valid format, correct scheme, and is in the whitelist.
fn is_apub_id_valid(apub_id: &Url) -> bool {
if apub_id.scheme() != get_apub_protocol_string() {
2020-04-17 17:34:18 +00:00
return false;
}
let whitelist: Vec<String> = Settings::get()
.federation
.instance_whitelist
.split(',')
.map(|d| d.to_string())
.collect();
match apub_id.domain() {
2020-04-17 17:34:18 +00:00
Some(d) => whitelist.contains(&d.to_owned()),
None => false,
}
}
2020-04-24 19:55:54 +00:00
// TODO Not sure good names for these
pub trait ToApub<Response> {
fn to_apub(&self, conn: &PgConnection) -> Result<Response, Error>;
}
pub trait FromApub<ApubType> {
fn from_apub(apub: &ApubType, conn: &PgConnection) -> Result<Self, Error>
where
Self: Sized;
}
pub trait ActorType {
fn actor_id(&self) -> String;
fn get_inbox_url(&self) -> String {
format!("{}/inbox", &self.actor_id())
}
fn get_outbox_url(&self) -> String {
format!("{}/outbox", &self.actor_id())
}
fn get_followers_url(&self) -> String {
format!("{}/followers", &self.actor_id())
}
fn get_following_url(&self) -> String {
format!("{}/following", &self.actor_id())
}
fn get_liked_url(&self) -> String {
format!("{}/liked", &self.actor_id())
}
}