forked from mirrors/relay
Add commandline blocking and whitelisting
This commit is contained in:
parent
3c5154d449
commit
eefff20941
14 changed files with 723 additions and 283 deletions
1
.env
1
.env
|
@ -1,2 +1 @@
|
||||||
DATABASE_URL=postgres://ap_actix:ap_actix@localhost:5432/ap_actix
|
DATABASE_URL=postgres://ap_actix:ap_actix@localhost:5432/ap_actix
|
||||||
HOSTNAME=localhost:8080
|
|
||||||
|
|
294
Cargo.lock
generated
294
Cargo.lock
generated
|
@ -9,7 +9,7 @@ dependencies = [
|
||||||
"activitystreams-derive",
|
"activitystreams-derive",
|
||||||
"chrono",
|
"chrono",
|
||||||
"mime",
|
"mime",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
"url",
|
||||||
|
@ -127,7 +127,7 @@ dependencies = [
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"sha-1",
|
"sha-1",
|
||||||
|
@ -155,7 +155,7 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"log",
|
"log",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -298,7 +298,7 @@ dependencies = [
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"regex",
|
"regex",
|
||||||
"rustls",
|
"rustls",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"time 0.2.9",
|
"time 0.2.9",
|
||||||
|
@ -324,7 +324,7 @@ checksum = "120ce509b4ad2a0dedfbaebc1c1fb2b5e7bb34430a851c3eb264a704135e30a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-http",
|
"actix-http",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
@ -364,6 +364,15 @@ dependencies = [
|
||||||
"memchr",
|
"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]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.27"
|
version = "1.0.27"
|
||||||
|
@ -382,6 +391,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.12.0",
|
"base64 0.12.0",
|
||||||
"bb8-postgres",
|
"bb8-postgres",
|
||||||
|
"config",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"futures",
|
"futures",
|
||||||
"http-signature-normalization-actix",
|
"http-signature-normalization-actix",
|
||||||
|
@ -393,9 +403,10 @@ dependencies = [
|
||||||
"rsa",
|
"rsa",
|
||||||
"rsa-magic-public-key",
|
"rsa-magic-public-key",
|
||||||
"rsa-pem",
|
"rsa-pem",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"structopt",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"ttl_cache",
|
"ttl_cache",
|
||||||
|
@ -408,6 +419,15 @@ version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825"
|
checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||||
|
dependencies = [
|
||||||
|
"nodrop",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
|
@ -461,7 +481,7 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand",
|
"rand",
|
||||||
"rustls",
|
"rustls",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
]
|
]
|
||||||
|
@ -635,10 +655,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
|
checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits 0.2.11",
|
||||||
"time 0.1.42",
|
"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]]
|
[[package]]
|
||||||
name = "cloudabi"
|
name = "cloudabi"
|
||||||
version = "0.0.3"
|
version = "0.0.3"
|
||||||
|
@ -648,6 +683,22 @@ dependencies = [
|
||||||
"bitflags",
|
"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]]
|
[[package]]
|
||||||
name = "const-random"
|
name = "const-random"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
|
@ -1183,6 +1234,19 @@ dependencies = [
|
||||||
"spin",
|
"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]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.68"
|
version = "0.2.68"
|
||||||
|
@ -1195,6 +1259,16 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
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]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -1234,7 +1308,7 @@ version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map 0.5.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1335,6 +1409,23 @@ dependencies = [
|
||||||
"winapi 0.3.8",
|
"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]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
|
@ -1343,7 +1434,7 @@ checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.0",
|
"autocfg 1.0.0",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits 0.2.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1358,9 +1449,9 @@ dependencies = [
|
||||||
"libm",
|
"libm",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-iter",
|
"num-iter",
|
||||||
"num-traits",
|
"num-traits 0.2.11",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
@ -1372,7 +1463,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
|
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.0",
|
"autocfg 1.0.0",
|
||||||
"num-traits",
|
"num-traits 0.2.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1383,7 +1474,16 @@ checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.0",
|
"autocfg 1.0.0",
|
||||||
"num-integer",
|
"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]]
|
[[package]]
|
||||||
|
@ -1547,6 +1647,32 @@ dependencies = [
|
||||||
"log",
|
"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]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.12"
|
version = "0.5.12"
|
||||||
|
@ -1690,7 +1816,7 @@ dependencies = [
|
||||||
"num-bigint-dig",
|
"num-bigint-dig",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-iter",
|
"num-iter",
|
||||||
"num-traits",
|
"num-traits 0.2.11",
|
||||||
"rand",
|
"rand",
|
||||||
"subtle 2.2.2",
|
"subtle 2.2.2",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
@ -1716,13 +1842,19 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-bigint-dig",
|
"num-bigint-dig",
|
||||||
"num-traits",
|
"num-traits 0.2.11",
|
||||||
"pem",
|
"pem",
|
||||||
"rsa",
|
"rsa",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"yasna",
|
"yasna",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-ini"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
|
@ -1799,6 +1931,12 @@ version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "0.8.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.105"
|
version = "1.0.105"
|
||||||
|
@ -1808,6 +1946,19 @@ dependencies = [
|
||||||
"serde_derive",
|
"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]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.105"
|
version = "1.0.105"
|
||||||
|
@ -1827,7 +1978,16 @@ checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"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]]
|
[[package]]
|
||||||
|
@ -1838,7 +1998,7 @@ checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dtoa",
|
"dtoa",
|
||||||
"itoa",
|
"itoa",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1924,6 +2084,12 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4edf667ea8f60afc06d6aeec079d20d5800351109addec1faea678a8663da4e1"
|
checksum = "4edf667ea8f60afc06d6aeec079d20d5800351109addec1faea678a8663da4e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stdweb"
|
name = "stdweb"
|
||||||
version = "0.4.20"
|
version = "0.4.20"
|
||||||
|
@ -1946,7 +2112,7 @@ checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
@ -1960,7 +2126,7 @@ dependencies = [
|
||||||
"base-x",
|
"base-x",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde 1.0.105",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha1",
|
"sha1",
|
||||||
|
@ -1983,6 +2149,36 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"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]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -2006,6 +2202,17 @@ dependencies = [
|
||||||
"unicode-xid",
|
"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]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
@ -2027,6 +2234,15 @@ dependencies = [
|
||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
@ -2196,6 +2412,15 @@ dependencies = [
|
||||||
"tokio",
|
"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]]
|
[[package]]
|
||||||
name = "trust-dns-proto"
|
name = "trust-dns-proto"
|
||||||
version = "0.19.3"
|
version = "0.19.3"
|
||||||
|
@ -2242,7 +2467,7 @@ version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4189890526f0168710b6ee65ceaedf1460c48a14318ceec933cb26baa492096a"
|
checksum = "4189890526f0168710b6ee65ceaedf1460c48a14318ceec933cb26baa492096a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map 0.5.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2275,6 +2500,12 @@ version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -2307,6 +2538,18 @@ dependencies = [
|
||||||
"rand",
|
"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]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.9.0+wasi-snapshot-preview1"
|
version = "0.9.0+wasi-snapshot-preview1"
|
||||||
|
@ -2464,6 +2707,15 @@ dependencies = [
|
||||||
"winapi-build",
|
"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]]
|
[[package]]
|
||||||
name = "yasna"
|
name = "yasna"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
|
@ -15,6 +15,7 @@ actix-webfinger = "0.3.0-alpha.3"
|
||||||
activitystreams = "0.5.0-alpha.11"
|
activitystreams = "0.5.0-alpha.11"
|
||||||
base64 = "0.12"
|
base64 = "0.12"
|
||||||
bb8-postgres = "0.4.0"
|
bb8-postgres = "0.4.0"
|
||||||
|
config = "0.10.1"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
futures = "0.3.4"
|
futures = "0.3.4"
|
||||||
http-signature-normalization-actix = { version = "0.3.0-alpha.5", default-features = false, features = ["sha-2"] }
|
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 = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
sha2 = "0.8"
|
sha2 = "0.8"
|
||||||
|
structopt = "0.3.12"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tokio = { version = "0.2.13", features = ["sync"] }
|
tokio = { version = "0.2.13", features = ["sync"] }
|
||||||
ttl_cache = "0.5.1"
|
ttl_cache = "0.5.1"
|
||||||
|
|
48
src/actor.rs
Normal file
48
src/actor.rs
Normal 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
32
src/args.rs
Normal 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
111
src/config.rs
Normal 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
114
src/db.rs
|
@ -2,7 +2,11 @@ use crate::error::MyError;
|
||||||
use activitystreams::primitives::XsdAnyUri;
|
use activitystreams::primitives::XsdAnyUri;
|
||||||
use bb8_postgres::{
|
use bb8_postgres::{
|
||||||
bb8,
|
bb8,
|
||||||
tokio_postgres::{row::Row, Client, Config, NoTls},
|
tokio_postgres::{
|
||||||
|
error::{Error, SqlState},
|
||||||
|
row::Row,
|
||||||
|
Client, Config, NoTls,
|
||||||
|
},
|
||||||
PostgresConnectionManager,
|
PostgresConnectionManager,
|
||||||
};
|
};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
@ -43,6 +47,48 @@ impl Db {
|
||||||
Ok(())
|
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> {
|
pub async fn hydrate_blocks(&self) -> Result<HashSet<String>, MyError> {
|
||||||
let conn = self.pool.get().await?;
|
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_blocks;");
|
||||||
info!("LISTEN new_whitelists;");
|
info!("LISTEN new_whitelists;");
|
||||||
info!("LISTEN new_listeners;");
|
info!("LISTEN new_listeners;");
|
||||||
|
@ -117,49 +163,67 @@ async fn update_private_key(client: &Client, key: &RSAPrivateKey) -> Result<(),
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_block(client: &Client, block: &XsdAnyUri) -> Result<(), MyError> {
|
async fn add_block(client: &Client, domain: &str) -> Result<(), Error> {
|
||||||
let host = if let Some(host) = block.as_url().host() {
|
|
||||||
host
|
|
||||||
} else {
|
|
||||||
return Err(MyError::Host(block.to_string()));
|
|
||||||
};
|
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
"INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
||||||
host.to_string()
|
domain,
|
||||||
);
|
);
|
||||||
client
|
client
|
||||||
.execute(
|
.execute(
|
||||||
"INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now');",
|
"INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now');",
|
||||||
&[&host.to_string()],
|
&[&domain],
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_whitelist(client: &Client, whitelist: &XsdAnyUri) -> Result<(), MyError> {
|
async fn remove_block(client: &Client, domain: &str) -> Result<(), Error> {
|
||||||
let host = if let Some(host) = whitelist.as_url().host() {
|
info!(
|
||||||
host
|
"DELETE FROM blocks WHERE domain_name = $1::TEXT; [{}]",
|
||||||
} else {
|
domain,
|
||||||
return Err(MyError::Host(whitelist.to_string()));
|
);
|
||||||
};
|
client
|
||||||
|
.execute(
|
||||||
|
"DELETE FROM blocks WHERE domain_name = $1::TEXT;",
|
||||||
|
&[&domain],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_whitelist(client: &Client, domain: &str) -> Result<(), Error> {
|
||||||
info!(
|
info!(
|
||||||
"INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
"INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
||||||
host.to_string()
|
domain,
|
||||||
);
|
);
|
||||||
client
|
client
|
||||||
.execute(
|
.execute(
|
||||||
"INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now');",
|
"INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now');",
|
||||||
&[&host.to_string()],
|
&[&domain],
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
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!(
|
info!(
|
||||||
"DELETE FROM listeners WHERE actor_id = {};",
|
"DELETE FROM listeners WHERE actor_id = {};",
|
||||||
listener.as_str()
|
listener.as_str()
|
||||||
|
@ -174,7 +238,7 @@ async fn remove_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), My
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), MyError> {
|
async fn add_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), Error> {
|
||||||
info!(
|
info!(
|
||||||
"INSERT INTO listeners (actor_id, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
"INSERT INTO listeners (actor_id, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
||||||
listener.as_str(),
|
listener.as_str(),
|
||||||
|
@ -189,14 +253,14 @@ async fn add_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), MyErr
|
||||||
Ok(())
|
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");
|
info!("SELECT domain_name FROM blocks");
|
||||||
let rows = client.query("SELECT domain_name FROM blocks", &[]).await?;
|
let rows = client.query("SELECT domain_name FROM blocks", &[]).await?;
|
||||||
|
|
||||||
parse_rows(rows)
|
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");
|
info!("SELECT domain_name FROM whitelists");
|
||||||
let rows = client
|
let rows = client
|
||||||
.query("SELECT domain_name FROM whitelists", &[])
|
.query("SELECT domain_name FROM whitelists", &[])
|
||||||
|
@ -205,14 +269,14 @@ async fn hydrate_whitelists(client: &Client) -> Result<HashSet<String>, MyError>
|
||||||
parse_rows(rows)
|
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");
|
info!("SELECT actor_id FROM listeners");
|
||||||
let rows = client.query("SELECT actor_id FROM listeners", &[]).await?;
|
let rows = client.query("SELECT actor_id FROM listeners", &[]).await?;
|
||||||
|
|
||||||
parse_rows(rows)
|
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
|
where
|
||||||
T: std::str::FromStr<Err = E> + Eq + std::hash::Hash,
|
T: std::str::FromStr<Err = E> + Eq + std::hash::Hash,
|
||||||
E: std::fmt::Display,
|
E: std::fmt::Display,
|
||||||
|
|
29
src/error.rs
29
src/error.rs
|
@ -1,11 +1,18 @@
|
||||||
use activitystreams::primitives::XsdAnyUriError;
|
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 log::error;
|
||||||
use rsa_pem::KeyError;
|
use rsa_pem::KeyError;
|
||||||
use std::{convert::Infallible, io::Error};
|
use std::{convert::Infallible, fmt::Debug, io::Error};
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum MyError {
|
pub enum MyError {
|
||||||
|
#[error("Error in configuration, {0}")]
|
||||||
|
Config(#[from] config::ConfigError),
|
||||||
|
|
||||||
#[error("Error in db, {0}")]
|
#[error("Error in db, {0}")]
|
||||||
DbError(#[from] bb8_postgres::tokio_postgres::error::Error),
|
DbError(#[from] bb8_postgres::tokio_postgres::error::Error),
|
||||||
|
|
||||||
|
@ -51,9 +58,6 @@ pub enum MyError {
|
||||||
#[error("Wrong ActivityPub kind, {0}")]
|
#[error("Wrong ActivityPub kind, {0}")]
|
||||||
Kind(String),
|
Kind(String),
|
||||||
|
|
||||||
#[error("No host present in URI, {0}")]
|
|
||||||
Host(String),
|
|
||||||
|
|
||||||
#[error("Too many CPUs, {0}")]
|
#[error("Too many CPUs, {0}")]
|
||||||
CpuCount(#[from] std::num::TryFromIntError),
|
CpuCount(#[from] std::num::TryFromIntError),
|
||||||
|
|
||||||
|
@ -77,6 +81,9 @@ pub enum MyError {
|
||||||
|
|
||||||
#[error("URI is missing domain field")]
|
#[error("URI is missing domain field")]
|
||||||
Domain,
|
Domain,
|
||||||
|
|
||||||
|
#[error("Blocking operation was canceled")]
|
||||||
|
Canceled,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for MyError {
|
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
|
impl<T> From<bb8_postgres::bb8::RunError<T>> for MyError
|
||||||
where
|
where
|
||||||
T: Into<MyError>,
|
T: Into<MyError>,
|
||||||
|
|
50
src/inbox.rs
50
src/inbox.rs
|
@ -1,10 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
accepted,
|
|
||||||
apub::{AcceptedActors, AcceptedObjects, ValidTypes},
|
apub::{AcceptedActors, AcceptedObjects, ValidTypes},
|
||||||
|
config::{Config, UrlKind},
|
||||||
db::Db,
|
db::Db,
|
||||||
error::MyError,
|
error::MyError,
|
||||||
requests::Requests,
|
requests::Requests,
|
||||||
state::{State, UrlKind},
|
responses::accepted,
|
||||||
|
state::State,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Accept, Announce, Follow, Undo},
|
activity::{Accept, Announce, Follow, Undo},
|
||||||
|
@ -22,6 +23,7 @@ use std::convert::TryInto;
|
||||||
pub async fn inbox(
|
pub async fn inbox(
|
||||||
db: web::Data<Db>,
|
db: web::Data<Db>,
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
|
config: web::Data<Config>,
|
||||||
client: web::Data<Requests>,
|
client: web::Data<Requests>,
|
||||||
input: web::Json<AcceptedObjects>,
|
input: web::Json<AcceptedObjects>,
|
||||||
verified: SignatureVerified,
|
verified: SignatureVerified,
|
||||||
|
@ -58,19 +60,20 @@ pub async fn inbox(
|
||||||
|
|
||||||
match input.kind {
|
match input.kind {
|
||||||
ValidTypes::Announce | ValidTypes::Create => {
|
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 => {
|
ValidTypes::Delete | ValidTypes::Update => {
|
||||||
handle_forward(&state, &client, input, actor).await
|
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(
|
async fn handle_undo(
|
||||||
db: &Db,
|
db: &Db,
|
||||||
state: &State,
|
state: &State,
|
||||||
|
config: &Config,
|
||||||
client: &Requests,
|
client: &Requests,
|
||||||
input: AcceptedObjects,
|
input: AcceptedObjects,
|
||||||
actor: AcceptedActors,
|
actor: AcceptedActors,
|
||||||
|
@ -88,7 +91,7 @@ async fn handle_undo(
|
||||||
return handle_forward(state, client, input, actor).await;
|
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()) {
|
if !input.object.child_object_is(&my_id) && !input.object.child_object_is(&public()) {
|
||||||
return Err(MyError::WrongActor(input.object.id().to_string()));
|
return Err(MyError::WrongActor(input.object.id().to_string()));
|
||||||
|
@ -97,7 +100,7 @@ async fn handle_undo(
|
||||||
let inbox = actor.inbox().to_owned();
|
let inbox = actor.inbox().to_owned();
|
||||||
db.remove_listener(inbox).await?;
|
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 client2 = client.clone();
|
||||||
let inbox = actor.inbox().clone();
|
let inbox = actor.inbox().clone();
|
||||||
|
@ -125,6 +128,7 @@ async fn handle_forward(
|
||||||
|
|
||||||
async fn handle_announce(
|
async fn handle_announce(
|
||||||
state: &State,
|
state: &State,
|
||||||
|
config: &Config,
|
||||||
client: &Requests,
|
client: &Requests,
|
||||||
input: AcceptedObjects,
|
input: AcceptedObjects,
|
||||||
actor: AcceptedActors,
|
actor: AcceptedActors,
|
||||||
|
@ -135,9 +139,9 @@ async fn handle_announce(
|
||||||
return Err(MyError::Duplicate);
|
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?;
|
let inboxes = get_inboxes(state, &actor, &object_id).await?;
|
||||||
client.deliver_many(inboxes, announce.clone());
|
client.deliver_many(inboxes, announce.clone());
|
||||||
|
|
||||||
|
@ -148,13 +152,13 @@ async fn handle_announce(
|
||||||
|
|
||||||
async fn handle_follow(
|
async fn handle_follow(
|
||||||
db: &Db,
|
db: &Db,
|
||||||
state: &State,
|
config: &Config,
|
||||||
client: &Requests,
|
client: &Requests,
|
||||||
input: AcceptedObjects,
|
input: AcceptedObjects,
|
||||||
actor: AcceptedActors,
|
actor: AcceptedActors,
|
||||||
is_listener: bool,
|
is_listener: bool,
|
||||||
) -> Result<HttpResponse, MyError> {
|
) -> 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()) {
|
if !input.object.is(&my_id) && !input.object.is(&public()) {
|
||||||
return Err(MyError::WrongActor(input.object.id().to_string()));
|
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 following relay directly, not just following 'public', followback
|
||||||
if input.object.is(&my_id) {
|
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 client2 = client.clone();
|
||||||
let inbox = actor.inbox().clone();
|
let inbox = actor.inbox().clone();
|
||||||
let follow2 = follow.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 client2 = client.clone();
|
||||||
let inbox = actor.inbox().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"
|
// Generate a type that says "I want to stop following you"
|
||||||
fn generate_undo_follow(
|
fn generate_undo_follow(
|
||||||
state: &State,
|
config: &Config,
|
||||||
actor_id: &XsdAnyUri,
|
actor_id: &XsdAnyUri,
|
||||||
my_id: &XsdAnyUri,
|
my_id: &XsdAnyUri,
|
||||||
) -> Result<Undo, MyError> {
|
) -> Result<Undo, MyError> {
|
||||||
|
@ -203,7 +207,7 @@ fn generate_undo_follow(
|
||||||
|
|
||||||
follow
|
follow
|
||||||
.object_props
|
.object_props
|
||||||
.set_id(state.generate_url(UrlKind::Activity))?;
|
.set_id(config.generate_url(UrlKind::Activity))?;
|
||||||
follow
|
follow
|
||||||
.follow_props
|
.follow_props
|
||||||
.set_actor_xsd_any_uri(actor_id.clone())?
|
.set_actor_xsd_any_uri(actor_id.clone())?
|
||||||
|
@ -212,12 +216,12 @@ fn generate_undo_follow(
|
||||||
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"
|
// Generate a type that says "Look at this object"
|
||||||
fn generate_announce(
|
fn generate_announce(
|
||||||
state: &State,
|
config: &Config,
|
||||||
activity_id: &XsdAnyUri,
|
activity_id: &XsdAnyUri,
|
||||||
object_id: &XsdAnyUri,
|
object_id: &XsdAnyUri,
|
||||||
) -> Result<Announce, MyError> {
|
) -> Result<Announce, MyError> {
|
||||||
|
@ -226,18 +230,18 @@ fn generate_announce(
|
||||||
announce
|
announce
|
||||||
.announce_props
|
.announce_props
|
||||||
.set_object_xsd_any_uri(object_id.clone())?
|
.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(
|
prepare_activity(
|
||||||
announce,
|
announce,
|
||||||
activity_id.clone(),
|
activity_id.clone(),
|
||||||
state.generate_url(UrlKind::Followers),
|
config.generate_url(UrlKind::Followers),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a type that says "I want to follow you"
|
// Generate a type that says "I want to follow you"
|
||||||
fn generate_follow(
|
fn generate_follow(
|
||||||
state: &State,
|
config: &Config,
|
||||||
actor_id: &XsdAnyUri,
|
actor_id: &XsdAnyUri,
|
||||||
my_id: &XsdAnyUri,
|
my_id: &XsdAnyUri,
|
||||||
) -> Result<Follow, MyError> {
|
) -> Result<Follow, MyError> {
|
||||||
|
@ -250,14 +254,14 @@ fn generate_follow(
|
||||||
|
|
||||||
prepare_activity(
|
prepare_activity(
|
||||||
follow,
|
follow,
|
||||||
state.generate_url(UrlKind::Activity),
|
config.generate_url(UrlKind::Activity),
|
||||||
actor_id.clone(),
|
actor_id.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a type that says "I accept your follow request"
|
// Generate a type that says "I accept your follow request"
|
||||||
fn generate_accept_follow(
|
fn generate_accept_follow(
|
||||||
state: &State,
|
config: &Config,
|
||||||
actor_id: &XsdAnyUri,
|
actor_id: &XsdAnyUri,
|
||||||
input_id: &XsdAnyUri,
|
input_id: &XsdAnyUri,
|
||||||
my_id: &XsdAnyUri,
|
my_id: &XsdAnyUri,
|
||||||
|
@ -281,7 +285,7 @@ fn generate_accept_follow(
|
||||||
|
|
||||||
prepare_activity(
|
prepare_activity(
|
||||||
accept,
|
accept,
|
||||||
state.generate_url(UrlKind::Activity),
|
config.generate_url(UrlKind::Activity),
|
||||||
actor_id.clone(),
|
actor_id.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
117
src/main.rs
117
src/main.rs
|
@ -1,122 +1,77 @@
|
||||||
use activitystreams::{
|
use actix_web::{middleware::Logger, web, App, HttpServer, Responder};
|
||||||
actor::Application, context, endpoint::EndpointProperties, ext::Extensible,
|
|
||||||
object::properties::ObjectProperties, security,
|
|
||||||
};
|
|
||||||
use actix_web::{middleware::Logger, web, App, HttpResponse, HttpServer, Responder};
|
|
||||||
use bb8_postgres::tokio_postgres;
|
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 apub;
|
||||||
|
mod args;
|
||||||
|
mod config;
|
||||||
mod db;
|
mod db;
|
||||||
mod error;
|
mod error;
|
||||||
mod inbox;
|
mod inbox;
|
||||||
mod nodeinfo;
|
mod nodeinfo;
|
||||||
mod notify;
|
mod notify;
|
||||||
mod requests;
|
mod requests;
|
||||||
|
mod responses;
|
||||||
mod state;
|
mod state;
|
||||||
mod verifier;
|
mod verifier;
|
||||||
mod webfinger;
|
mod webfinger;
|
||||||
|
|
||||||
use self::{
|
use self::{args::Args, config::Config, db::Db, state::State, webfinger::RelayResolver};
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn index() -> impl Responder {
|
async fn index() -> impl Responder {
|
||||||
"hewwo, mr obama"
|
"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]
|
#[actix_rt::main]
|
||||||
async fn main() -> Result<(), anyhow::Error> {
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
dotenv::dotenv().ok();
|
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();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
let pg_config: tokio_postgres::Config = std::env::var("DATABASE_URL")?.parse()?;
|
let pg_config: tokio_postgres::Config = config.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 db = Db::build(pg_config.clone()).await?;
|
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 _ = notify::NotifyHandler::start_handler(state.clone(), pg_config.clone());
|
||||||
|
|
||||||
|
let bind_address = config.bind_address();
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
let state = state.clone();
|
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Logger::default())
|
.wrap(Logger::default())
|
||||||
.data(db.clone())
|
.data(db.clone())
|
||||||
.data(state.clone())
|
.data(state.clone())
|
||||||
.data(state.requests())
|
.data(state.requests())
|
||||||
|
.data(config.clone())
|
||||||
.service(web::resource("/").route(web::get().to(index)))
|
.service(web::resource("/").route(web::get().to(index)))
|
||||||
.service(
|
.service(
|
||||||
web::resource("/inbox")
|
web::resource("/inbox")
|
||||||
.wrap(VerifyDigest::new(Sha256::new()))
|
.wrap(config.digest_middleware())
|
||||||
.wrap(VerifySignature::new(
|
.wrap(config.signature_middleware(state.requests()))
|
||||||
MyVerify(state.requests()),
|
|
||||||
Default::default(),
|
|
||||||
))
|
|
||||||
.route(web::post().to(inbox::inbox)),
|
.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::resource("/nodeinfo/2.0").route(web::get().to(nodeinfo::route)))
|
||||||
.service(
|
.service(
|
||||||
web::scope("/.well-known")
|
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))),
|
.service(web::resource("/nodeinfo").route(web::get().to(nodeinfo::well_known))),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.bind("0.0.0.0:8080")?
|
.bind(bind_address)?
|
||||||
.run()
|
.run()
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
use crate::state::{State, UrlKind};
|
use crate::config::{Config, UrlKind};
|
||||||
use actix_web::{web, Responder};
|
use actix_web::{web, Responder};
|
||||||
use actix_webfinger::Link;
|
use actix_webfinger::Link;
|
||||||
use std::collections::HashMap;
|
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 {
|
web::Json(Link {
|
||||||
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0".to_owned(),
|
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,
|
template: None,
|
||||||
kind: None,
|
kind: None,
|
||||||
})
|
})
|
||||||
.with_header("Content-Type", "application/jrd+json")
|
.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 {
|
web::Json(NodeInfo {
|
||||||
version: NodeInfoVersion,
|
version: NodeInfoVersion,
|
||||||
software: Software {
|
software: Software {
|
||||||
name: state.software_name(),
|
name: config.software_name(),
|
||||||
version: state.software_version(),
|
version: config.software_version(),
|
||||||
},
|
},
|
||||||
protocols: vec![Protocol::ActivityPub],
|
protocols: vec![Protocol::ActivityPub],
|
||||||
services: vec![],
|
services: vec![],
|
||||||
|
|
20
src/responses.rs
Normal file
20
src/responses.rs
Normal 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)
|
||||||
|
}
|
155
src/state.rs
155
src/state.rs
|
@ -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 activitystreams::primitives::XsdAnyUri;
|
||||||
|
use actix_web::web;
|
||||||
use futures::try_join;
|
use futures::try_join;
|
||||||
use log::info;
|
use log::info;
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
|
@ -8,13 +15,14 @@ use rsa::{RSAPrivateKey, RSAPublicKey};
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::{collections::HashSet, sync::Arc};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use ttl_cache::TtlCache;
|
use ttl_cache::TtlCache;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub type ActorCache = Arc<RwLock<TtlCache<XsdAnyUri, AcceptedActors>>>;
|
pub type ActorCache = Arc<RwLock<TtlCache<XsdAnyUri, AcceptedActors>>>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub settings: Settings,
|
pub public_key: RSAPublicKey,
|
||||||
|
private_key: RSAPrivateKey,
|
||||||
|
config: Config,
|
||||||
actor_cache: ActorCache,
|
actor_cache: ActorCache,
|
||||||
actor_id_cache: Arc<RwLock<LruCache<XsdAnyUri, XsdAnyUri>>>,
|
actor_id_cache: Arc<RwLock<LruCache<XsdAnyUri, XsdAnyUri>>>,
|
||||||
blocks: Arc<RwLock<HashSet<String>>>,
|
blocks: Arc<RwLock<HashSet<String>>>,
|
||||||
|
@ -22,112 +30,20 @@ pub struct State {
|
||||||
listeners: Arc<RwLock<HashSet<XsdAnyUri>>>,
|
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 {
|
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 {
|
pub fn requests(&self) -> Requests {
|
||||||
Requests::new(
|
Requests::new(
|
||||||
self.generate_url(UrlKind::MainKey),
|
self.config.generate_url(UrlKind::MainKey),
|
||||||
self.settings.private_key.clone(),
|
self.private_key.clone(),
|
||||||
self.actor_cache.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) {
|
pub async fn bust_whitelist(&self, whitelist: &str) {
|
||||||
let hs = self.whitelists.clone();
|
let hs = self.whitelists.clone();
|
||||||
|
|
||||||
|
@ -169,7 +85,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_whitelisted(&self, actor_id: &XsdAnyUri) -> bool {
|
pub async fn is_whitelisted(&self, actor_id: &XsdAnyUri) -> bool {
|
||||||
if !self.settings.whitelist_enabled {
|
if !self.config.whitelist_mode() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,21 +152,36 @@ impl State {
|
||||||
write_guard.insert(listener);
|
write_guard.insert(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn hydrate(
|
pub async fn hydrate(config: Config, db: &Db) -> Result<Self, MyError> {
|
||||||
use_https: bool,
|
|
||||||
whitelist_enabled: bool,
|
|
||||||
hostname: String,
|
|
||||||
db: &Db,
|
|
||||||
) -> Result<Self, MyError> {
|
|
||||||
let f1 = db.hydrate_blocks();
|
let f1 = db.hydrate_blocks();
|
||||||
let f2 = db.hydrate_whitelists();
|
let f2 = db.hydrate_whitelists();
|
||||||
let f3 = db.hydrate_listeners();
|
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 {
|
Ok(State {
|
||||||
settings,
|
public_key,
|
||||||
|
private_key,
|
||||||
|
config,
|
||||||
actor_cache: Arc::new(RwLock::new(TtlCache::new(1024 * 8))),
|
actor_cache: Arc::new(RwLock::new(TtlCache::new(1024 * 8))),
|
||||||
actor_id_cache: Arc::new(RwLock::new(LruCache::new(1024 * 8))),
|
actor_id_cache: Arc::new(RwLock::new(LruCache::new(1024 * 8))),
|
||||||
blocks: Arc::new(RwLock::new(blocks)),
|
blocks: Arc::new(RwLock::new(blocks)),
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::state::{State, UrlKind};
|
use crate::{
|
||||||
|
config::{Config, UrlKind},
|
||||||
|
state::State,
|
||||||
|
};
|
||||||
use activitystreams::context;
|
use activitystreams::context;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_webfinger::{Link, Resolver, Webfinger};
|
use actix_webfinger::{Link, Resolver, Webfinger};
|
||||||
|
@ -11,19 +14,19 @@ pub struct RelayResolver;
|
||||||
#[error("Error resolving webfinger data")]
|
#[error("Error resolving webfinger data")]
|
||||||
pub struct RelayError;
|
pub struct RelayError;
|
||||||
|
|
||||||
impl Resolver<Data<State>> for RelayResolver {
|
impl Resolver<(Data<State>, Data<Config>)> for RelayResolver {
|
||||||
type Error = RelayError;
|
type Error = RelayError;
|
||||||
|
|
||||||
fn find(
|
fn find(
|
||||||
account: &str,
|
account: &str,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
state: Data<State>,
|
(state, config): (Data<State>, Data<Config>),
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Option<Webfinger>, Self::Error>>>> {
|
) -> Pin<Box<dyn Future<Output = Result<Option<Webfinger>, Self::Error>>>> {
|
||||||
let domain = domain.to_owned();
|
let domain = domain.to_owned();
|
||||||
let account = account.to_owned();
|
let account = account.to_owned();
|
||||||
|
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
if domain != state.settings.hostname {
|
if domain != config.hostname() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,13 +34,13 @@ impl Resolver<Data<State>> for RelayResolver {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut wf = Webfinger::new(&state.generate_resource());
|
let mut wf = Webfinger::new(&config.generate_resource());
|
||||||
wf.add_alias(&state.generate_url(UrlKind::Actor))
|
wf.add_alias(&config.generate_url(UrlKind::Actor))
|
||||||
.add_activitypub(&state.generate_url(UrlKind::Actor))
|
.add_activitypub(&config.generate_url(UrlKind::Actor))
|
||||||
.add_magic_public_key(&state.settings.public_key.as_magic_public_key())
|
.add_magic_public_key(&state.public_key.as_magic_public_key())
|
||||||
.add_link(Link {
|
.add_link(Link {
|
||||||
rel: "self".to_owned(),
|
rel: "self".to_owned(),
|
||||||
href: Some(state.generate_url(UrlKind::Actor)),
|
href: Some(config.generate_url(UrlKind::Actor)),
|
||||||
template: None,
|
template: None,
|
||||||
kind: Some(format!("application/ld+json; profile=\"{}\"", context())),
|
kind: Some(format!("application/ld+json; profile=\"{}\"", context())),
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue