mirror of
https://git.asonix.dog/asonix/relay.git
synced 2024-12-23 11:40:31 +00:00
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
|
||||
HOSTNAME=localhost:8080
|
||||
|
|
294
Cargo.lock
generated
294
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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
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 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,
|
||||
|
|
29
src/error.rs
29
src/error.rs
|
@ -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>,
|
||||
|
|
50
src/inbox.rs
50
src/inbox.rs
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
|
117
src/main.rs
117
src/main.rs
|
@ -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(())
|
||||
|
|
|
@ -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
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 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)),
|
||||
|
|
|
@ -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())),
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue