2020-04-09 19:04:31 +00:00
|
|
|
pub mod activities;
|
2019-12-19 21:59:13 +00:00
|
|
|
pub mod community;
|
2020-04-15 18:12:25 +00:00
|
|
|
pub mod community_inbox;
|
2020-04-10 11:37:35 +00:00
|
|
|
pub mod fetcher;
|
2019-12-19 21:59:13 +00:00
|
|
|
pub mod post;
|
2020-04-10 13:50:40 +00:00
|
|
|
pub mod signatures;
|
2019-12-19 21:59:13 +00:00
|
|
|
pub mod user;
|
2020-04-15 18:12:25 +00:00
|
|
|
pub mod user_inbox;
|
2020-04-10 13:50:40 +00:00
|
|
|
use crate::apub::signatures::PublicKeyExtension;
|
2019-12-19 21:59:13 +00:00
|
|
|
use crate::Settings;
|
2020-04-07 21:02:32 +00:00
|
|
|
use activitystreams::actor::{properties::ApActorProperties, Group, Person};
|
2020-04-03 05:02:43 +00:00
|
|
|
use activitystreams::ext::Ext;
|
2020-03-16 17:30:25 +00:00
|
|
|
use actix_web::body::Body;
|
|
|
|
use actix_web::HttpResponse;
|
2020-04-10 12:45:48 +00:00
|
|
|
use openssl::{pkey::PKey, rsa::Rsa};
|
2020-04-17 15:33:55 +00:00
|
|
|
use serde::ser::Serialize;
|
2020-03-12 00:01:25 +00:00
|
|
|
use url::Url;
|
2019-12-19 21:59:13 +00:00
|
|
|
|
2020-04-10 13:50:40 +00:00
|
|
|
type GroupExt = Ext<Ext<Group, ApActorProperties>, PublicKeyExtension>;
|
|
|
|
type PersonExt = Ext<Ext<Person, ApActorProperties>, PublicKeyExtension>;
|
2020-04-03 05:02:43 +00:00
|
|
|
|
2020-04-07 15:29:23 +00:00
|
|
|
static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
|
|
|
|
|
2020-04-08 16:22:44 +00:00
|
|
|
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()
|
2020-04-07 15:29:23 +00:00
|
|
|
.content_type(APUB_JSON_CONTENT_TYPE)
|
2020-04-17 15:33:55 +00:00
|
|
|
.json(data)
|
2019-12-19 21:59:13 +00:00
|
|
|
}
|
|
|
|
|
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.
|
2020-04-03 04:12:05 +00:00
|
|
|
pub fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url {
|
2020-03-16 18:19:04 +00:00
|
|
|
let point = match endpoint_type {
|
2020-04-07 15:34:44 +00:00
|
|
|
EndpointType::Community => "c",
|
|
|
|
EndpointType::User => "u",
|
|
|
|
EndpointType::Post => "p",
|
2020-04-14 19:12:19 +00:00
|
|
|
// TODO I have to change this else my update advanced_migrations crashes the
|
|
|
|
// server if a comment exists.
|
|
|
|
EndpointType::Comment => "comment",
|
2020-03-16 18:19:04 +00:00
|
|
|
};
|
|
|
|
|
2020-03-12 00:01:25 +00:00
|
|
|
Url::parse(&format!(
|
2020-02-29 17:38:47 +00:00
|
|
|
"{}://{}/federation/{}/{}",
|
|
|
|
get_apub_protocol_string(),
|
2019-12-19 21:59:13 +00:00
|
|
|
Settings::get().hostname,
|
|
|
|
point,
|
2020-03-16 18:19:04 +00:00
|
|
|
name
|
2020-03-12 00:01:25 +00:00
|
|
|
))
|
|
|
|
.unwrap()
|
2019-12-19 21:59:13 +00:00
|
|
|
}
|
2020-02-29 17:38:47 +00:00
|
|
|
|
2020-03-18 15:08:08 +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-03 04:12:05 +00:00
|
|
|
|
2020-04-17 15:33:55 +00:00
|
|
|
/// Generate the asymmetric keypair for ActivityPub HTTP signatures.
|
|
|
|
pub fn gen_keypair_str() -> (String, String) {
|
2020-04-03 04:12:05 +00:00
|
|
|
let rsa = Rsa::generate(2048).expect("sign::gen_keypair: key generation error");
|
|
|
|
let pkey = PKey::from_rsa(rsa).expect("sign::gen_keypair: parsing error");
|
2020-04-17 15:33:55 +00:00
|
|
|
let public_key = pkey
|
|
|
|
.public_key_to_pem()
|
|
|
|
.expect("sign::gen_keypair: public key encoding error");
|
|
|
|
let private_key = pkey
|
|
|
|
.private_key_to_pem_pkcs8()
|
|
|
|
.expect("sign::gen_keypair: private key encoding error");
|
2020-04-03 04:12:05 +00:00
|
|
|
(vec_bytes_to_str(public_key), vec_bytes_to_str(private_key))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn vec_bytes_to_str(bytes: Vec<u8>) -> String {
|
|
|
|
String::from_utf8_lossy(&bytes).into_owned()
|
|
|
|
}
|
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: &str) -> bool {
|
|
|
|
let url = match Url::parse(apub_id) {
|
|
|
|
Ok(u) => u,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
if url.scheme() != get_apub_protocol_string() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let whitelist: Vec<String> = Settings::get()
|
|
|
|
.federation
|
|
|
|
.instance_whitelist
|
|
|
|
.split(',')
|
|
|
|
.map(|d| d.to_string())
|
|
|
|
.collect();
|
|
|
|
match url.domain() {
|
|
|
|
Some(d) => whitelist.contains(&d.to_owned()),
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|