Add commandline blocking and whitelisting

This commit is contained in:
asonix 2020-03-19 19:55:11 -05:00
parent 3c5154d449
commit eefff20941
14 changed files with 723 additions and 283 deletions

1
.env
View file

@ -1,2 +1 @@
DATABASE_URL=postgres://ap_actix:ap_actix@localhost:5432/ap_actix
HOSTNAME=localhost:8080

294
Cargo.lock generated
View file

@ -9,7 +9,7 @@ dependencies = [
"activitystreams-derive",
"chrono",
"mime",
"serde",
"serde 1.0.105",
"serde_json",
"thiserror",
"url",
@ -127,7 +127,7 @@ dependencies = [
"pin-project",
"rand",
"regex",
"serde",
"serde 1.0.105",
"serde_json",
"serde_urlencoded",
"sha-1",
@ -155,7 +155,7 @@ dependencies = [
"http",
"log",
"regex",
"serde",
"serde 1.0.105",
]
[[package]]
@ -298,7 +298,7 @@ dependencies = [
"pin-project",
"regex",
"rustls",
"serde",
"serde 1.0.105",
"serde_json",
"serde_urlencoded",
"time 0.2.9",
@ -324,7 +324,7 @@ checksum = "120ce509b4ad2a0dedfbaebc1c1fb2b5e7bb34430a851c3eb264a704135e30a7"
dependencies = [
"actix-http",
"actix-web",
"serde",
"serde 1.0.105",
"serde_derive",
"thiserror",
]
@ -364,6 +364,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "anyhow"
version = "1.0.27"
@ -382,6 +391,7 @@ dependencies = [
"anyhow",
"base64 0.12.0",
"bb8-postgres",
"config",
"dotenv",
"futures",
"http-signature-normalization-actix",
@ -393,9 +403,10 @@ dependencies = [
"rsa",
"rsa-magic-public-key",
"rsa-pem",
"serde",
"serde 1.0.105",
"serde_json",
"sha2",
"structopt",
"thiserror",
"tokio",
"ttl_cache",
@ -408,6 +419,15 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825"
[[package]]
name = "arrayvec"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
dependencies = [
"nodrop",
]
[[package]]
name = "async-trait"
version = "0.1.24"
@ -461,7 +481,7 @@ dependencies = [
"percent-encoding",
"rand",
"rustls",
"serde",
"serde 1.0.105",
"serde_json",
"serde_urlencoded",
]
@ -635,10 +655,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
dependencies = [
"num-integer",
"num-traits",
"num-traits 0.2.11",
"time 0.1.42",
]
[[package]]
name = "clap"
version = "2.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
@ -648,6 +683,22 @@ dependencies = [
"bitflags",
]
[[package]]
name = "config"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3"
dependencies = [
"lazy_static",
"nom",
"rust-ini",
"serde 1.0.105",
"serde-hjson",
"serde_json",
"toml",
"yaml-rust",
]
[[package]]
name = "const-random"
version = "0.1.8"
@ -1183,6 +1234,19 @@ dependencies = [
"spin",
]
[[package]]
name = "lexical-core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7043aa5c05dd34fb73b47acb8c3708eac428de4545ea3682ed2f11293ebd890"
dependencies = [
"arrayvec",
"cfg-if",
"rustc_version",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.68"
@ -1195,6 +1259,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
[[package]]
name = "linked-hash-map"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
dependencies = [
"serde 0.8.23",
"serde_test",
]
[[package]]
name = "linked-hash-map"
version = "0.5.2"
@ -1234,7 +1308,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
dependencies = [
"linked-hash-map",
"linked-hash-map 0.5.2",
]
[[package]]
@ -1335,6 +1409,23 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "5.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6"
dependencies = [
"lexical-core",
"memchr",
"version_check",
]
[[package]]
name = "num-bigint"
version = "0.2.6"
@ -1343,7 +1434,7 @@ checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
"autocfg 1.0.0",
"num-integer",
"num-traits",
"num-traits 0.2.11",
]
[[package]]
@ -1358,9 +1449,9 @@ dependencies = [
"libm",
"num-integer",
"num-iter",
"num-traits",
"num-traits 0.2.11",
"rand",
"serde",
"serde 1.0.105",
"smallvec",
"zeroize",
]
@ -1372,7 +1463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
dependencies = [
"autocfg 1.0.0",
"num-traits",
"num-traits 0.2.11",
]
[[package]]
@ -1383,7 +1474,16 @@ checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
dependencies = [
"autocfg 1.0.0",
"num-integer",
"num-traits",
"num-traits 0.2.11",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.11",
]
[[package]]
@ -1547,6 +1647,32 @@ dependencies = [
"log",
]
[[package]]
name = "proc-macro-error"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn-mid",
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.12"
@ -1690,7 +1816,7 @@ dependencies = [
"num-bigint-dig",
"num-integer",
"num-iter",
"num-traits",
"num-traits 0.2.11",
"rand",
"subtle 2.2.2",
"zeroize",
@ -1716,13 +1842,19 @@ dependencies = [
"log",
"num-bigint",
"num-bigint-dig",
"num-traits",
"num-traits 0.2.11",
"pem",
"rsa",
"thiserror",
"yasna",
]
[[package]]
name = "rust-ini"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
[[package]]
name = "rustc-demangle"
version = "0.1.16"
@ -1799,6 +1931,12 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
[[package]]
name = "serde"
version = "1.0.105"
@ -1808,6 +1946,19 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-hjson"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8"
dependencies = [
"lazy_static",
"linked-hash-map 0.3.0",
"num-traits 0.1.43",
"regex",
"serde 0.8.23",
]
[[package]]
name = "serde_derive"
version = "1.0.105"
@ -1827,7 +1978,16 @@ checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
dependencies = [
"itoa",
"ryu",
"serde",
"serde 1.0.105",
]
[[package]]
name = "serde_test"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5"
dependencies = [
"serde 0.8.23",
]
[[package]]
@ -1838,7 +1998,7 @@ checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
dependencies = [
"dtoa",
"itoa",
"serde",
"serde 1.0.105",
"url",
]
@ -1924,6 +2084,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4edf667ea8f60afc06d6aeec079d20d5800351109addec1faea678a8663da4e1"
[[package]]
name = "static_assertions"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
[[package]]
name = "stdweb"
version = "0.4.20"
@ -1946,7 +2112,7 @@ checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
dependencies = [
"proc-macro2",
"quote",
"serde",
"serde 1.0.105",
"serde_derive",
"syn",
]
@ -1960,7 +2126,7 @@ dependencies = [
"base-x",
"proc-macro2",
"quote",
"serde",
"serde 1.0.105",
"serde_derive",
"serde_json",
"sha1",
@ -1983,6 +2149,36 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8faa2719539bbe9d77869bfb15d4ee769f99525e707931452c97b693b3f159d"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f88b8e18c69496aad6f9ddf4630dd7d585bcaf765786cb415b9aec2fe5a0430"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "subtle"
version = "1.0.0"
@ -2006,6 +2202,17 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "syn-mid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "synstructure"
version = "0.12.3"
@ -2027,6 +2234,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.11"
@ -2196,6 +2412,15 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
dependencies = [
"serde 1.0.105",
]
[[package]]
name = "trust-dns-proto"
version = "0.19.3"
@ -2242,7 +2467,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4189890526f0168710b6ee65ceaedf1460c48a14318ceec933cb26baa492096a"
dependencies = [
"linked-hash-map",
"linked-hash-map 0.5.2",
]
[[package]]
@ -2275,6 +2500,12 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
[[package]]
name = "unicode-xid"
version = "0.2.0"
@ -2307,6 +2538,18 @@ dependencies = [
"rand",
]
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@ -2464,6 +2707,15 @@ dependencies = [
"winapi-build",
]
[[package]]
name = "yaml-rust"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
dependencies = [
"linked-hash-map 0.5.2",
]
[[package]]
name = "yasna"
version = "0.3.1"

View file

@ -15,6 +15,7 @@ actix-webfinger = "0.3.0-alpha.3"
activitystreams = "0.5.0-alpha.11"
base64 = "0.12"
bb8-postgres = "0.4.0"
config = "0.10.1"
dotenv = "0.15.0"
futures = "0.3.4"
http-signature-normalization-actix = { version = "0.3.0-alpha.5", default-features = false, features = ["sha-2"] }
@ -29,6 +30,7 @@ rsa-pem = { version = "0.1.0", git = "https://git.asonix.dog/Aardwolf/rsa-pem" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha2 = "0.8"
structopt = "0.3.12"
thiserror = "1.0"
tokio = { version = "0.2.13", features = ["sync"] }
ttl_cache = "0.5.1"

48
src/actor.rs Normal file
View file

@ -0,0 +1,48 @@
use crate::{
apub::PublicKey,
config::{Config, UrlKind},
error::MyError,
responses::ok,
state::State,
};
use activitystreams::{
actor::Application, context, endpoint::EndpointProperties, ext::Extensible,
object::properties::ObjectProperties, security,
};
use actix_web::{web, Responder};
use rsa_pem::KeyExt;
pub async fn route(
state: web::Data<State>,
config: web::Data<Config>,
) -> Result<impl Responder, MyError> {
let mut application = Application::full();
let mut endpoint = EndpointProperties::default();
endpoint.set_shared_inbox(config.generate_url(UrlKind::Inbox))?;
let props: &mut ObjectProperties = application.as_mut();
props
.set_id(config.generate_url(UrlKind::Actor))?
.set_summary_xsd_string("AodeRelay bot")?
.set_name_xsd_string("AodeRelay")?
.set_url_xsd_any_uri(config.generate_url(UrlKind::Actor))?
.set_many_context_xsd_any_uris(vec![context(), security()])?;
application
.extension
.set_preferred_username("relay")?
.set_followers(config.generate_url(UrlKind::Followers))?
.set_following(config.generate_url(UrlKind::Following))?
.set_inbox(config.generate_url(UrlKind::Inbox))?
.set_outbox(config.generate_url(UrlKind::Outbox))?
.set_endpoints(endpoint)?;
let public_key = PublicKey {
id: config.generate_url(UrlKind::MainKey).parse()?,
owner: config.generate_url(UrlKind::Actor).parse()?,
public_key_pem: state.public_key.to_pem_pkcs8()?,
};
Ok(ok(application.extend(public_key.to_ext())))
}

32
src/args.rs Normal file
View file

@ -0,0 +1,32 @@
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "relay", about = "An activitypub relay")]
pub struct Args {
#[structopt(short, help = "A list of domains that should be blocked")]
blocks: Vec<String>,
#[structopt(short, help = "A list of domains that should be whitelisted")]
whitelists: Vec<String>,
#[structopt(short, help = "Undo whitelisting or blocking these domains")]
undo: bool,
}
impl Args {
pub fn new() -> Self {
Self::from_args()
}
pub fn blocks(&self) -> &[String] {
&self.blocks
}
pub fn whitelists(&self) -> &[String] {
&self.whitelists
}
pub fn undo(&self) -> bool {
self.undo
}
}

111
src/config.rs Normal file
View file

@ -0,0 +1,111 @@
use crate::{error::MyError, requests::Requests, verifier::MyVerify};
use config::Environment;
use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature};
use sha2::{Digest, Sha256};
use std::net::IpAddr;
use uuid::Uuid;
#[derive(Clone, Debug, serde::Deserialize)]
pub struct Config {
hostname: String,
addr: IpAddr,
port: u16,
debug: bool,
whitelist_mode: bool,
validate_signatures: bool,
https: bool,
database_url: String,
}
pub enum UrlKind {
Activity,
Actor,
Followers,
Following,
Inbox,
MainKey,
NodeInfo,
Outbox,
}
impl Config {
pub fn build() -> Result<Self, MyError> {
let mut config = config::Config::new();
config
.set_default("hostname", "localhost:8080")?
.set_default("addr", "127.0.0.1")?
.set_default("port", 8080)?
.set_default("debug", true)?
.set_default("whitelist_mode", false)?
.set_default("validate_signatures", false)?
.set_default("https", false)?
.merge(Environment::new())?;
Ok(config.try_into()?)
}
pub fn digest_middleware(&self) -> VerifyDigest<Sha256> {
if self.validate_signatures {
VerifyDigest::new(Sha256::new())
} else {
VerifyDigest::new(Sha256::new()).optional()
}
}
pub fn signature_middleware(&self, requests: Requests) -> VerifySignature<MyVerify> {
if self.validate_signatures {
VerifySignature::new(MyVerify(requests), Default::default())
} else {
VerifySignature::new(MyVerify(requests), Default::default()).optional()
}
}
pub fn bind_address(&self) -> (IpAddr, u16) {
(self.addr, self.port)
}
pub fn debug(&self) -> bool {
self.debug
}
pub fn whitelist_mode(&self) -> bool {
self.whitelist_mode
}
pub fn database_url(&self) -> &str {
&self.database_url
}
pub fn hostname(&self) -> &str {
&self.hostname
}
pub fn generate_resource(&self) -> String {
format!("relay@{}", self.hostname)
}
pub fn software_name(&self) -> String {
"AodeRelay".to_owned()
}
pub fn software_version(&self) -> String {
"v0.1.0-master".to_owned()
}
pub fn generate_url(&self, kind: UrlKind) -> String {
let scheme = if self.https { "https" } else { "http" };
match kind {
UrlKind::Activity => {
format!("{}://{}/activity/{}", scheme, self.hostname, Uuid::new_v4())
}
UrlKind::Actor => format!("{}://{}/actor", scheme, self.hostname),
UrlKind::Followers => format!("{}://{}/followers", scheme, self.hostname),
UrlKind::Following => format!("{}://{}/following", scheme, self.hostname),
UrlKind::Inbox => format!("{}://{}/inbox", scheme, self.hostname),
UrlKind::MainKey => format!("{}://{}/actor#main-key", scheme, self.hostname),
UrlKind::NodeInfo => format!("{}://{}/nodeinfo/2.0", scheme, self.hostname),
UrlKind::Outbox => format!("{}://{}/outbox", scheme, self.hostname),
}
}
}

114
src/db.rs
View file

@ -2,7 +2,11 @@ use crate::error::MyError;
use activitystreams::primitives::XsdAnyUri;
use bb8_postgres::{
bb8,
tokio_postgres::{row::Row, Client, Config, NoTls},
tokio_postgres::{
error::{Error, SqlState},
row::Row,
Client, Config, NoTls,
},
PostgresConnectionManager,
};
use log::{info, warn};
@ -43,6 +47,48 @@ impl Db {
Ok(())
}
pub async fn add_blocks(&self, domains: &[String]) -> Result<(), MyError> {
let conn = self.pool.get().await?;
for domain in domains {
match add_block(&conn, domain.as_str()).await {
Err(e) if e.code() != Some(&SqlState::UNIQUE_VIOLATION) => {
Err(e)?;
}
_ => (),
};
}
Ok(())
}
pub async fn remove_blocks(&self, domains: &[String]) -> Result<(), MyError> {
let conn = self.pool.get().await?;
for domain in domains {
remove_block(&conn, domain.as_str()).await?
}
Ok(())
}
pub async fn add_whitelists(&self, domains: &[String]) -> Result<(), MyError> {
let conn = self.pool.get().await?;
for domain in domains {
match add_whitelist(&conn, domain.as_str()).await {
Err(e) if e.code() != Some(&SqlState::UNIQUE_VIOLATION) => {
Err(e)?;
}
_ => (),
};
}
Ok(())
}
pub async fn remove_whitelists(&self, domains: &[String]) -> Result<(), MyError> {
let conn = self.pool.get().await?;
for domain in domains {
remove_whitelist(&conn, domain.as_str()).await?
}
Ok(())
}
pub async fn hydrate_blocks(&self) -> Result<HashSet<String>, MyError> {
let conn = self.pool.get().await?;
@ -74,7 +120,7 @@ impl Db {
}
}
pub async fn listen(client: &Client) -> Result<(), MyError> {
pub async fn listen(client: &Client) -> Result<(), Error> {
info!("LISTEN new_blocks;");
info!("LISTEN new_whitelists;");
info!("LISTEN new_listeners;");
@ -117,49 +163,67 @@ async fn update_private_key(client: &Client, key: &RSAPrivateKey) -> Result<(),
Ok(())
}
async fn add_block(client: &Client, block: &XsdAnyUri) -> Result<(), MyError> {
let host = if let Some(host) = block.as_url().host() {
host
} else {
return Err(MyError::Host(block.to_string()));
};
async fn add_block(client: &Client, domain: &str) -> Result<(), Error> {
info!(
"INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]",
host.to_string()
domain,
);
client
.execute(
"INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now');",
&[&host.to_string()],
&[&domain],
)
.await?;
Ok(())
}
async fn add_whitelist(client: &Client, whitelist: &XsdAnyUri) -> Result<(), MyError> {
let host = if let Some(host) = whitelist.as_url().host() {
host
} else {
return Err(MyError::Host(whitelist.to_string()));
};
async fn remove_block(client: &Client, domain: &str) -> Result<(), Error> {
info!(
"DELETE FROM blocks WHERE domain_name = $1::TEXT; [{}]",
domain,
);
client
.execute(
"DELETE FROM blocks WHERE domain_name = $1::TEXT;",
&[&domain],
)
.await?;
Ok(())
}
async fn add_whitelist(client: &Client, domain: &str) -> Result<(), Error> {
info!(
"INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]",
host.to_string()
domain,
);
client
.execute(
"INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now');",
&[&host.to_string()],
&[&domain],
)
.await?;
Ok(())
}
async fn remove_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), MyError> {
async fn remove_whitelist(client: &Client, domain: &str) -> Result<(), Error> {
info!(
"DELETE FROM whitelists WHERE domain_name = $1::TEXT; [{}]",
domain,
);
client
.execute(
"DELETE FROM whitelists WHERE domain_name = $1::TEXT;",
&[&domain],
)
.await?;
Ok(())
}
async fn remove_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), Error> {
info!(
"DELETE FROM listeners WHERE actor_id = {};",
listener.as_str()
@ -174,7 +238,7 @@ async fn remove_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), My
Ok(())
}
async fn add_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), MyError> {
async fn add_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), Error> {
info!(
"INSERT INTO listeners (actor_id, created_at) VALUES ($1::TEXT, 'now'); [{}]",
listener.as_str(),
@ -189,14 +253,14 @@ async fn add_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), MyErr
Ok(())
}
async fn hydrate_blocks(client: &Client) -> Result<HashSet<String>, MyError> {
async fn hydrate_blocks(client: &Client) -> Result<HashSet<String>, Error> {
info!("SELECT domain_name FROM blocks");
let rows = client.query("SELECT domain_name FROM blocks", &[]).await?;
parse_rows(rows)
}
async fn hydrate_whitelists(client: &Client) -> Result<HashSet<String>, MyError> {
async fn hydrate_whitelists(client: &Client) -> Result<HashSet<String>, Error> {
info!("SELECT domain_name FROM whitelists");
let rows = client
.query("SELECT domain_name FROM whitelists", &[])
@ -205,14 +269,14 @@ async fn hydrate_whitelists(client: &Client) -> Result<HashSet<String>, MyError>
parse_rows(rows)
}
async fn hydrate_listeners(client: &Client) -> Result<HashSet<XsdAnyUri>, MyError> {
async fn hydrate_listeners(client: &Client) -> Result<HashSet<XsdAnyUri>, Error> {
info!("SELECT actor_id FROM listeners");
let rows = client.query("SELECT actor_id FROM listeners", &[]).await?;
parse_rows(rows)
}
fn parse_rows<T, E>(rows: Vec<Row>) -> Result<HashSet<T>, MyError>
fn parse_rows<T, E>(rows: Vec<Row>) -> Result<HashSet<T>, Error>
where
T: std::str::FromStr<Err = E> + Eq + std::hash::Hash,
E: std::fmt::Display,

View file

@ -1,11 +1,18 @@
use activitystreams::primitives::XsdAnyUriError;
use actix_web::{error::ResponseError, http::StatusCode, HttpResponse};
use actix_web::{
error::{BlockingError, ResponseError},
http::StatusCode,
HttpResponse,
};
use log::error;
use rsa_pem::KeyError;
use std::{convert::Infallible, io::Error};
use std::{convert::Infallible, fmt::Debug, io::Error};
#[derive(Debug, thiserror::Error)]
pub enum MyError {
#[error("Error in configuration, {0}")]
Config(#[from] config::ConfigError),
#[error("Error in db, {0}")]
DbError(#[from] bb8_postgres::tokio_postgres::error::Error),
@ -51,9 +58,6 @@ pub enum MyError {
#[error("Wrong ActivityPub kind, {0}")]
Kind(String),
#[error("No host present in URI, {0}")]
Host(String),
#[error("Too many CPUs, {0}")]
CpuCount(#[from] std::num::TryFromIntError),
@ -77,6 +81,9 @@ pub enum MyError {
#[error("URI is missing domain field")]
Domain,
#[error("Blocking operation was canceled")]
Canceled,
}
impl ResponseError for MyError {
@ -102,6 +109,18 @@ impl ResponseError for MyError {
}
}
impl<T> From<BlockingError<T>> for MyError
where
T: Into<MyError> + Debug,
{
fn from(e: BlockingError<T>) -> Self {
match e {
BlockingError::Error(e) => e.into(),
BlockingError::Canceled => MyError::Canceled,
}
}
}
impl<T> From<bb8_postgres::bb8::RunError<T>> for MyError
where
T: Into<MyError>,

View file

@ -1,10 +1,11 @@
use crate::{
accepted,
apub::{AcceptedActors, AcceptedObjects, ValidTypes},
config::{Config, UrlKind},
db::Db,
error::MyError,
requests::Requests,
state::{State, UrlKind},
responses::accepted,
state::State,
};
use activitystreams::{
activity::{Accept, Announce, Follow, Undo},
@ -22,6 +23,7 @@ use std::convert::TryInto;
pub async fn inbox(
db: web::Data<Db>,
state: web::Data<State>,
config: web::Data<Config>,
client: web::Data<Requests>,
input: web::Json<AcceptedObjects>,
verified: SignatureVerified,
@ -58,19 +60,20 @@ pub async fn inbox(
match input.kind {
ValidTypes::Announce | ValidTypes::Create => {
handle_announce(&state, &client, input, actor).await
handle_announce(&state, &config, &client, input, actor).await
}
ValidTypes::Follow => handle_follow(&db, &state, &client, input, actor, is_listener).await,
ValidTypes::Follow => handle_follow(&db, &config, &client, input, actor, is_listener).await,
ValidTypes::Delete | ValidTypes::Update => {
handle_forward(&state, &client, input, actor).await
}
ValidTypes::Undo => handle_undo(&db, &state, &client, input, actor).await,
ValidTypes::Undo => handle_undo(&db, &state, &config, &client, input, actor).await,
}
}
async fn handle_undo(
db: &Db,
state: &State,
config: &Config,
client: &Requests,
input: AcceptedObjects,
actor: AcceptedActors,
@ -88,7 +91,7 @@ async fn handle_undo(
return handle_forward(state, client, input, actor).await;
}
let my_id: XsdAnyUri = state.generate_url(UrlKind::Actor).parse()?;
let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?;
if !input.object.child_object_is(&my_id) && !input.object.child_object_is(&public()) {
return Err(MyError::WrongActor(input.object.id().to_string()));
@ -97,7 +100,7 @@ async fn handle_undo(
let inbox = actor.inbox().to_owned();
db.remove_listener(inbox).await?;
let undo = generate_undo_follow(state, &actor.id, &my_id)?;
let undo = generate_undo_follow(config, &actor.id, &my_id)?;
let client2 = client.clone();
let inbox = actor.inbox().clone();
@ -125,6 +128,7 @@ async fn handle_forward(
async fn handle_announce(
state: &State,
config: &Config,
client: &Requests,
input: AcceptedObjects,
actor: AcceptedActors,
@ -135,9 +139,9 @@ async fn handle_announce(
return Err(MyError::Duplicate);
}
let activity_id: XsdAnyUri = state.generate_url(UrlKind::Activity).parse()?;
let activity_id: XsdAnyUri = config.generate_url(UrlKind::Activity).parse()?;
let announce = generate_announce(state, &activity_id, object_id)?;
let announce = generate_announce(config, &activity_id, object_id)?;
let inboxes = get_inboxes(state, &actor, &object_id).await?;
client.deliver_many(inboxes, announce.clone());
@ -148,13 +152,13 @@ async fn handle_announce(
async fn handle_follow(
db: &Db,
state: &State,
config: &Config,
client: &Requests,
input: AcceptedObjects,
actor: AcceptedActors,
is_listener: bool,
) -> Result<HttpResponse, MyError> {
let my_id: XsdAnyUri = state.generate_url(UrlKind::Actor).parse()?;
let my_id: XsdAnyUri = config.generate_url(UrlKind::Actor).parse()?;
if !input.object.is(&my_id) && !input.object.is(&public()) {
return Err(MyError::WrongActor(input.object.id().to_string()));
@ -166,7 +170,7 @@ async fn handle_follow(
// if following relay directly, not just following 'public', followback
if input.object.is(&my_id) {
let follow = generate_follow(state, &actor.id, &my_id)?;
let follow = generate_follow(config, &actor.id, &my_id)?;
let client2 = client.clone();
let inbox = actor.inbox().clone();
let follow2 = follow.clone();
@ -176,7 +180,7 @@ async fn handle_follow(
}
}
let accept = generate_accept_follow(state, &actor.id, &input.id, &my_id)?;
let accept = generate_accept_follow(config, &actor.id, &input.id, &my_id)?;
let client2 = client.clone();
let inbox = actor.inbox().clone();
@ -190,7 +194,7 @@ async fn handle_follow(
// Generate a type that says "I want to stop following you"
fn generate_undo_follow(
state: &State,
config: &Config,
actor_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<Undo, MyError> {
@ -203,7 +207,7 @@ fn generate_undo_follow(
follow
.object_props
.set_id(state.generate_url(UrlKind::Activity))?;
.set_id(config.generate_url(UrlKind::Activity))?;
follow
.follow_props
.set_actor_xsd_any_uri(actor_id.clone())?
@ -212,12 +216,12 @@ fn generate_undo_follow(
follow
})?;
prepare_activity(undo, state.generate_url(UrlKind::Actor), actor_id.clone())
prepare_activity(undo, config.generate_url(UrlKind::Actor), actor_id.clone())
}
// Generate a type that says "Look at this object"
fn generate_announce(
state: &State,
config: &Config,
activity_id: &XsdAnyUri,
object_id: &XsdAnyUri,
) -> Result<Announce, MyError> {
@ -226,18 +230,18 @@ fn generate_announce(
announce
.announce_props
.set_object_xsd_any_uri(object_id.clone())?
.set_actor_xsd_any_uri(state.generate_url(UrlKind::Actor))?;
.set_actor_xsd_any_uri(config.generate_url(UrlKind::Actor))?;
prepare_activity(
announce,
activity_id.clone(),
state.generate_url(UrlKind::Followers),
config.generate_url(UrlKind::Followers),
)
}
// Generate a type that says "I want to follow you"
fn generate_follow(
state: &State,
config: &Config,
actor_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<Follow, MyError> {
@ -250,14 +254,14 @@ fn generate_follow(
prepare_activity(
follow,
state.generate_url(UrlKind::Activity),
config.generate_url(UrlKind::Activity),
actor_id.clone(),
)
}
// Generate a type that says "I accept your follow request"
fn generate_accept_follow(
state: &State,
config: &Config,
actor_id: &XsdAnyUri,
input_id: &XsdAnyUri,
my_id: &XsdAnyUri,
@ -281,7 +285,7 @@ fn generate_accept_follow(
prepare_activity(
accept,
state.generate_url(UrlKind::Activity),
config.generate_url(UrlKind::Activity),
actor_id.clone(),
)
}

View file

@ -1,122 +1,77 @@
use activitystreams::{
actor::Application, context, endpoint::EndpointProperties, ext::Extensible,
object::properties::ObjectProperties, security,
};
use actix_web::{middleware::Logger, web, App, HttpResponse, HttpServer, Responder};
use actix_web::{middleware::Logger, web, App, HttpServer, Responder};
use bb8_postgres::tokio_postgres;
use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature};
use rsa_pem::KeyExt;
use sha2::{Digest, Sha256};
mod actor;
mod apub;
mod args;
mod config;
mod db;
mod error;
mod inbox;
mod nodeinfo;
mod notify;
mod requests;
mod responses;
mod state;
mod verifier;
mod webfinger;
use self::{
apub::PublicKey,
db::Db,
error::MyError,
state::{State, UrlKind},
verifier::MyVerify,
webfinger::RelayResolver,
};
pub fn ok<T>(item: T) -> HttpResponse
where
T: serde::ser::Serialize,
{
HttpResponse::Ok()
.content_type("application/activity+json")
.json(item)
}
pub fn accepted<T>(item: T) -> HttpResponse
where
T: serde::ser::Serialize,
{
HttpResponse::Accepted()
.content_type("application/activity+json")
.json(item)
}
use self::{args::Args, config::Config, db::Db, state::State, webfinger::RelayResolver};
async fn index() -> impl Responder {
"hewwo, mr obama"
}
async fn actor_route(state: web::Data<State>) -> Result<impl Responder, MyError> {
let mut application = Application::full();
let mut endpoint = EndpointProperties::default();
endpoint.set_shared_inbox(state.generate_url(UrlKind::Inbox))?;
let props: &mut ObjectProperties = application.as_mut();
props
.set_id(state.generate_url(UrlKind::Actor))?
.set_summary_xsd_string("AodeRelay bot")?
.set_name_xsd_string("AodeRelay")?
.set_url_xsd_any_uri(state.generate_url(UrlKind::Actor))?
.set_many_context_xsd_any_uris(vec![context(), security()])?;
application
.extension
.set_preferred_username("relay")?
.set_followers(state.generate_url(UrlKind::Followers))?
.set_following(state.generate_url(UrlKind::Following))?
.set_inbox(state.generate_url(UrlKind::Inbox))?
.set_outbox(state.generate_url(UrlKind::Outbox))?
.set_endpoints(endpoint)?;
let public_key = PublicKey {
id: state.generate_url(UrlKind::MainKey).parse()?,
owner: state.generate_url(UrlKind::Actor).parse()?,
public_key_pem: state.settings.public_key.to_pem_pkcs8()?,
};
Ok(ok(application.extend(public_key.to_ext())))
}
#[actix_rt::main]
async fn main() -> Result<(), anyhow::Error> {
dotenv::dotenv().ok();
let config = Config::build()?;
if config.debug() {
std::env::set_var("RUST_LOG", "debug")
} else {
std::env::set_var("RUST_LOG", "info")
}
pretty_env_logger::init();
let pg_config: tokio_postgres::Config = std::env::var("DATABASE_URL")?.parse()?;
let hostname: String = std::env::var("HOSTNAME")?;
let use_whitelist = std::env::var("USE_WHITELIST").is_ok();
let use_https = std::env::var("USE_HTTPS").is_ok();
let pg_config: tokio_postgres::Config = config.database_url().parse()?;
let db = Db::build(pg_config.clone()).await?;
let state = State::hydrate(use_https, use_whitelist, hostname, &db).await?;
let args = Args::new();
if !args.blocks().is_empty() || !args.whitelists().is_empty() {
if args.undo() {
db.remove_blocks(args.blocks()).await?;
db.remove_whitelists(args.whitelists()).await?;
} else {
db.add_blocks(args.blocks()).await?;
db.add_whitelists(args.whitelists()).await?;
}
return Ok(());
}
let state = State::hydrate(config.clone(), &db).await?;
let _ = notify::NotifyHandler::start_handler(state.clone(), pg_config.clone());
let bind_address = config.bind_address();
HttpServer::new(move || {
let state = state.clone();
App::new()
.wrap(Logger::default())
.data(db.clone())
.data(state.clone())
.data(state.requests())
.data(config.clone())
.service(web::resource("/").route(web::get().to(index)))
.service(
web::resource("/inbox")
.wrap(VerifyDigest::new(Sha256::new()))
.wrap(VerifySignature::new(
MyVerify(state.requests()),
Default::default(),
))
.wrap(config.digest_middleware())
.wrap(config.signature_middleware(state.requests()))
.route(web::post().to(inbox::inbox)),
)
.service(web::resource("/actor").route(web::get().to(actor_route)))
.service(web::resource("/actor").route(web::get().to(actor::route)))
.service(web::resource("/nodeinfo/2.0").route(web::get().to(nodeinfo::route)))
.service(
web::scope("/.well-known")
@ -124,7 +79,7 @@ async fn main() -> Result<(), anyhow::Error> {
.service(web::resource("/nodeinfo").route(web::get().to(nodeinfo::well_known))),
)
})
.bind("0.0.0.0:8080")?
.bind(bind_address)?
.run()
.await?;
Ok(())

View file

@ -1,24 +1,24 @@
use crate::state::{State, UrlKind};
use crate::config::{Config, UrlKind};
use actix_web::{web, Responder};
use actix_webfinger::Link;
use std::collections::HashMap;
pub async fn well_known(state: web::Data<State>) -> impl Responder {
pub async fn well_known(config: web::Data<Config>) -> impl Responder {
web::Json(Link {
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0".to_owned(),
href: Some(state.generate_url(UrlKind::NodeInfo)),
href: Some(config.generate_url(UrlKind::NodeInfo)),
template: None,
kind: None,
})
.with_header("Content-Type", "application/jrd+json")
}
pub async fn route(state: web::Data<State>) -> web::Json<NodeInfo> {
pub async fn route(config: web::Data<Config>) -> web::Json<NodeInfo> {
web::Json(NodeInfo {
version: NodeInfoVersion,
software: Software {
name: state.software_name(),
version: state.software_version(),
name: config.software_name(),
version: config.software_version(),
},
protocols: vec![Protocol::ActivityPub],
services: vec![],

20
src/responses.rs Normal file
View file

@ -0,0 +1,20 @@
use actix_web::HttpResponse;
use serde::ser::Serialize;
static CONTENT_TYPE: &str = "application/activity+json";
pub fn ok<T>(item: T) -> HttpResponse
where
T: Serialize,
{
HttpResponse::Ok().content_type(CONTENT_TYPE).json(item)
}
pub fn accepted<T>(item: T) -> HttpResponse
where
T: Serialize,
{
HttpResponse::Accepted()
.content_type(CONTENT_TYPE)
.json(item)
}

View file

@ -1,5 +1,12 @@
use crate::{apub::AcceptedActors, db::Db, error::MyError, requests::Requests};
use crate::{
apub::AcceptedActors,
config::{Config, UrlKind},
db::Db,
error::MyError,
requests::Requests,
};
use activitystreams::primitives::XsdAnyUri;
use actix_web::web;
use futures::try_join;
use log::info;
use lru::LruCache;
@ -8,13 +15,14 @@ use rsa::{RSAPrivateKey, RSAPublicKey};
use std::{collections::HashSet, sync::Arc};
use tokio::sync::RwLock;
use ttl_cache::TtlCache;
use uuid::Uuid;
pub type ActorCache = Arc<RwLock<TtlCache<XsdAnyUri, AcceptedActors>>>;
#[derive(Clone)]
pub struct State {
pub settings: Settings,
pub public_key: RSAPublicKey,
private_key: RSAPrivateKey,
config: Config,
actor_cache: ActorCache,
actor_id_cache: Arc<RwLock<LruCache<XsdAnyUri, XsdAnyUri>>>,
blocks: Arc<RwLock<HashSet<String>>>,
@ -22,112 +30,20 @@ pub struct State {
listeners: Arc<RwLock<HashSet<XsdAnyUri>>>,
}
#[derive(Clone)]
pub struct Settings {
pub use_https: bool,
pub whitelist_enabled: bool,
pub hostname: String,
pub public_key: RSAPublicKey,
private_key: RSAPrivateKey,
}
pub enum UrlKind {
Activity,
Actor,
Followers,
Following,
Inbox,
MainKey,
NodeInfo,
Outbox,
}
impl Settings {
async fn hydrate(
db: &Db,
use_https: bool,
whitelist_enabled: bool,
hostname: String,
) -> Result<Self, MyError> {
let private_key = if let Some(key) = db.hydrate_private_key().await? {
key
} else {
info!("Generating new keys");
let mut rng = thread_rng();
let key = RSAPrivateKey::new(&mut rng, 4096)?;
db.update_private_key(&key).await?;
key
};
let public_key = private_key.to_public_key();
Ok(Settings {
use_https,
whitelist_enabled,
hostname,
private_key,
public_key,
})
}
fn generate_url(&self, kind: UrlKind) -> String {
let scheme = if self.use_https { "https" } else { "http" };
match kind {
UrlKind::Activity => {
format!("{}://{}/activity/{}", scheme, self.hostname, Uuid::new_v4())
}
UrlKind::Actor => format!("{}://{}/actor", scheme, self.hostname),
UrlKind::Followers => format!("{}://{}/followers", scheme, self.hostname),
UrlKind::Following => format!("{}://{}/following", scheme, self.hostname),
UrlKind::Inbox => format!("{}://{}/inbox", scheme, self.hostname),
UrlKind::MainKey => format!("{}://{}/actor#main-key", scheme, self.hostname),
UrlKind::NodeInfo => format!("{}://{}/nodeinfo/2.0", scheme, self.hostname),
UrlKind::Outbox => format!("{}://{}/outbox", scheme, self.hostname),
}
}
fn generate_resource(&self) -> String {
format!("relay@{}", self.hostname)
}
fn software_name(&self) -> String {
"AodeRelay".to_owned()
}
fn software_version(&self) -> String {
"v0.1.0-master".to_owned()
}
}
impl State {
pub fn software_name(&self) -> String {
self.settings.software_name()
}
pub fn software_version(&self) -> String {
self.settings.software_version()
}
pub fn requests(&self) -> Requests {
Requests::new(
self.generate_url(UrlKind::MainKey),
self.settings.private_key.clone(),
self.config.generate_url(UrlKind::MainKey),
self.private_key.clone(),
self.actor_cache.clone(),
format!("{} {}", self.software_name(), self.software_version()),
format!(
"{} {}",
self.config.software_name(),
self.config.software_version()
),
)
}
pub fn generate_url(&self, kind: UrlKind) -> String {
self.settings.generate_url(kind)
}
pub fn generate_resource(&self) -> String {
self.settings.generate_resource()
}
pub async fn bust_whitelist(&self, whitelist: &str) {
let hs = self.whitelists.clone();
@ -169,7 +85,7 @@ impl State {
}
pub async fn is_whitelisted(&self, actor_id: &XsdAnyUri) -> bool {
if !self.settings.whitelist_enabled {
if !self.config.whitelist_mode() {
return true;
}
@ -236,21 +152,36 @@ impl State {
write_guard.insert(listener);
}
pub async fn hydrate(
use_https: bool,
whitelist_enabled: bool,
hostname: String,
db: &Db,
) -> Result<Self, MyError> {
pub async fn hydrate(config: Config, db: &Db) -> Result<Self, MyError> {
let f1 = db.hydrate_blocks();
let f2 = db.hydrate_whitelists();
let f3 = db.hydrate_listeners();
let f4 = Settings::hydrate(db, use_https, whitelist_enabled, hostname);
let (blocks, whitelists, listeners, settings) = try_join!(f1, f2, f3, f4)?;
let f4 = async move {
if let Some(key) = db.hydrate_private_key().await? {
Ok(key)
} else {
info!("Generating new keys");
let key = web::block(move || {
let mut rng = thread_rng();
RSAPrivateKey::new(&mut rng, 4096)
})
.await?;
db.update_private_key(&key).await?;
Ok(key)
}
};
let (blocks, whitelists, listeners, private_key) = try_join!(f1, f2, f3, f4)?;
let public_key = private_key.to_public_key();
Ok(State {
settings,
public_key,
private_key,
config,
actor_cache: Arc::new(RwLock::new(TtlCache::new(1024 * 8))),
actor_id_cache: Arc::new(RwLock::new(LruCache::new(1024 * 8))),
blocks: Arc::new(RwLock::new(blocks)),

View file

@ -1,4 +1,7 @@
use crate::state::{State, UrlKind};
use crate::{
config::{Config, UrlKind},
state::State,
};
use activitystreams::context;
use actix_web::web::Data;
use actix_webfinger::{Link, Resolver, Webfinger};
@ -11,19 +14,19 @@ pub struct RelayResolver;
#[error("Error resolving webfinger data")]
pub struct RelayError;
impl Resolver<Data<State>> for RelayResolver {
impl Resolver<(Data<State>, Data<Config>)> for RelayResolver {
type Error = RelayError;
fn find(
account: &str,
domain: &str,
state: Data<State>,
(state, config): (Data<State>, Data<Config>),
) -> Pin<Box<dyn Future<Output = Result<Option<Webfinger>, Self::Error>>>> {
let domain = domain.to_owned();
let account = account.to_owned();
let fut = async move {
if domain != state.settings.hostname {
if domain != config.hostname() {
return Ok(None);
}
@ -31,13 +34,13 @@ impl Resolver<Data<State>> for RelayResolver {
return Ok(None);
}
let mut wf = Webfinger::new(&state.generate_resource());
wf.add_alias(&state.generate_url(UrlKind::Actor))
.add_activitypub(&state.generate_url(UrlKind::Actor))
.add_magic_public_key(&state.settings.public_key.as_magic_public_key())
let mut wf = Webfinger::new(&config.generate_resource());
wf.add_alias(&config.generate_url(UrlKind::Actor))
.add_activitypub(&config.generate_url(UrlKind::Actor))
.add_magic_public_key(&state.public_key.as_magic_public_key())
.add_link(Link {
rel: "self".to_owned(),
href: Some(state.generate_url(UrlKind::Actor)),
href: Some(config.generate_url(UrlKind::Actor)),
template: None,
kind: Some(format!("application/ld+json; profile=\"{}\"", context())),
});