Add keys, http-sigs, webfinger
This commit is contained in:
		
							parent
							
								
									d741e2b202
								
							
						
					
					
						commit
						eea0577686
					
				
					 11 changed files with 561 additions and 80 deletions
				
			
		
							
								
								
									
										207
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										207
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -100,7 +100,7 @@ dependencies = [ | |||
|  "actix-threadpool", | ||||
|  "actix-tls", | ||||
|  "actix-utils", | ||||
|  "base64", | ||||
|  "base64 0.11.0", | ||||
|  "bitflags", | ||||
|  "brotli2", | ||||
|  "bytes", | ||||
|  | @ -313,6 +313,19 @@ dependencies = [ | |||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "actix-webfinger" | ||||
| version = "0.3.0-alpha.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "221e03224c654d7c6f35cc7a8bb7bc01ce0e53eb61b7722de199c6182cd9f3fe" | ||||
| dependencies = [ | ||||
|  "actix-http", | ||||
|  "actix-web", | ||||
|  "serde", | ||||
|  "serde_derive", | ||||
|  "thiserror", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "actix_derive" | ||||
| version = "0.5.0" | ||||
|  | @ -362,15 +375,23 @@ dependencies = [ | |||
|  "actix", | ||||
|  "actix-rt", | ||||
|  "actix-web", | ||||
|  "actix-webfinger", | ||||
|  "anyhow", | ||||
|  "base64 0.12.0", | ||||
|  "bb8-postgres", | ||||
|  "dotenv", | ||||
|  "futures", | ||||
|  "http-signature-normalization-actix", | ||||
|  "log", | ||||
|  "lru", | ||||
|  "pretty_env_logger", | ||||
|  "rand", | ||||
|  "rsa", | ||||
|  "rsa-magic-public-key", | ||||
|  "rsa-pem", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "sha2", | ||||
|  "thiserror", | ||||
|  "tokio", | ||||
|  "ttl_cache", | ||||
|  | @ -427,7 +448,7 @@ dependencies = [ | |||
|  "actix-http", | ||||
|  "actix-rt", | ||||
|  "actix-service", | ||||
|  "base64", | ||||
|  "base64 0.11.0", | ||||
|  "bytes", | ||||
|  "derive_more", | ||||
|  "futures-core", | ||||
|  | @ -475,6 +496,12 @@ version = "0.11.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "base64" | ||||
| version = "0.12.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bb8" | ||||
| version = "0.4.0" | ||||
|  | @ -674,7 +701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" | ||||
| dependencies = [ | ||||
|  "generic-array 0.12.3", | ||||
|  "subtle", | ||||
|  "subtle 1.0.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -1046,6 +1073,32 @@ dependencies = [ | |||
|  "itoa", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "http-signature-normalization" | ||||
| version = "0.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f2db9cb1c64aaabb27523433fc8df0467670df81642fd770086cf8a2eb11b24a" | ||||
| dependencies = [ | ||||
|  "chrono", | ||||
|  "thiserror", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "http-signature-normalization-actix" | ||||
| version = "0.3.0-alpha.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "406cae6778fb05b34885ee9aaec4e55931ac763aac3edf9a1e25bd40d4490e86" | ||||
| dependencies = [ | ||||
|  "actix-http", | ||||
|  "actix-web", | ||||
|  "base64 0.11.0", | ||||
|  "bytes", | ||||
|  "futures", | ||||
|  "http-signature-normalization", | ||||
|  "sha2", | ||||
|  "thiserror", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "httparse" | ||||
| version = "1.3.4" | ||||
|  | @ -1129,6 +1182,9 @@ name = "lazy_static" | |||
| version = "1.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||||
| dependencies = [ | ||||
|  "spin", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "libc" | ||||
|  | @ -1136,6 +1192,12 @@ version = "0.2.67" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "libm" | ||||
| version = "0.2.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "linked-hash-map" | ||||
| version = "0.5.2" | ||||
|  | @ -1276,6 +1338,36 @@ dependencies = [ | |||
|  "winapi 0.3.8", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "num-bigint" | ||||
| version = "0.2.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" | ||||
| dependencies = [ | ||||
|  "autocfg 1.0.0", | ||||
|  "num-integer", | ||||
|  "num-traits", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "num-bigint-dig" | ||||
| version = "0.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b3d03c330f9f7a2c19e3c0b42698e48141d0809c78cd9b6219f85bd7d7e892aa" | ||||
| dependencies = [ | ||||
|  "autocfg 0.1.7", | ||||
|  "byteorder", | ||||
|  "lazy_static", | ||||
|  "libm", | ||||
|  "num-integer", | ||||
|  "num-iter", | ||||
|  "num-traits", | ||||
|  "rand", | ||||
|  "serde", | ||||
|  "smallvec", | ||||
|  "zeroize", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "num-integer" | ||||
| version = "0.1.42" | ||||
|  | @ -1286,6 +1378,17 @@ dependencies = [ | |||
|  "num-traits", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "num-iter" | ||||
| version = "0.1.40" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" | ||||
| dependencies = [ | ||||
|  "autocfg 1.0.0", | ||||
|  "num-integer", | ||||
|  "num-traits", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "num-traits" | ||||
| version = "0.2.11" | ||||
|  | @ -1362,6 +1465,17 @@ dependencies = [ | |||
|  "winapi 0.3.8", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pem" | ||||
| version = "0.7.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a1581760c757a756a41f0ee3ff01256227bdf64cb752839779b95ffb01c59793" | ||||
| dependencies = [ | ||||
|  "base64 0.11.0", | ||||
|  "lazy_static", | ||||
|  "regex", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "percent-encoding" | ||||
| version = "2.1.0" | ||||
|  | @ -1430,7 +1544,7 @@ version = "0.5.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a30f0e172ae0fb0653dbf777ad10a74b8e58d6de95a892f2e1d3e94a9df9a844" | ||||
| dependencies = [ | ||||
|  "base64", | ||||
|  "base64 0.11.0", | ||||
|  "byteorder", | ||||
|  "bytes", | ||||
|  "fallible-iterator", | ||||
|  | @ -1586,6 +1700,49 @@ dependencies = [ | |||
|  "quick-error", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "rsa" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6ed8692d8e0ea3baae03f0f32ecfc13a6c6f1f85fcd6d9fdefcdf364e70f4df9" | ||||
| dependencies = [ | ||||
|  "byteorder", | ||||
|  "failure", | ||||
|  "lazy_static", | ||||
|  "num-bigint-dig", | ||||
|  "num-integer", | ||||
|  "num-iter", | ||||
|  "num-traits", | ||||
|  "rand", | ||||
|  "subtle 2.2.2", | ||||
|  "zeroize", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "rsa-magic-public-key" | ||||
| version = "0.1.0" | ||||
| source = "git+https://git.asonix.dog/Aardwolf/rsa-magic-public-key#82878d8274530c8184be8e82f0de36623b51d3f6" | ||||
| dependencies = [ | ||||
|  "base64 0.11.0", | ||||
|  "num-bigint-dig", | ||||
|  "rsa", | ||||
|  "thiserror", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "rsa-pem" | ||||
| version = "0.1.0" | ||||
| source = "git+https://git.asonix.dog/Aardwolf/rsa-pem#6c47c3fc377375a5bfedbb7457832fc013d3227d" | ||||
| dependencies = [ | ||||
|  "num-bigint", | ||||
|  "num-bigint-dig", | ||||
|  "num-traits", | ||||
|  "pem", | ||||
|  "rsa", | ||||
|  "thiserror", | ||||
|  "yasna", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "rustc-demangle" | ||||
| version = "0.1.16" | ||||
|  | @ -1752,6 +1909,12 @@ dependencies = [ | |||
|  "winapi 0.3.8", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "spin" | ||||
| version = "0.5.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "standback" | ||||
| version = "0.2.1" | ||||
|  | @ -1823,6 +1986,12 @@ version = "1.0.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "subtle" | ||||
| version = "2.2.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "1.0.16" | ||||
|  | @ -2260,3 +2429,33 @@ dependencies = [ | |||
|  "winapi 0.2.8", | ||||
|  "winapi-build", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "yasna" | ||||
| version = "0.3.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a563d10ead87e2d798e357d44f40f495ad70bcee4d5c0d3f77a5b1b7376645d9" | ||||
| dependencies = [ | ||||
|  "num-bigint", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zeroize" | ||||
| version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" | ||||
| dependencies = [ | ||||
|  "zeroize_derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zeroize_derive" | ||||
| version = "1.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
|  "synstructure", | ||||
| ] | ||||
|  |  | |||
							
								
								
									
										13
									
								
								Cargo.toml
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								Cargo.toml
									
										
									
									
									
								
							|  | @ -9,18 +9,29 @@ edition = "2018" | |||
| [dependencies] | ||||
| anyhow = "1.0" | ||||
| actix = "0.10.0-alpha.2" | ||||
| actix-web = { version = "3.0.0-alpha.1", features = ["openssl"] } | ||||
| actix-rt = "1.0.0" | ||||
| actix-web = { version = "3.0.0-alpha.1", features = ["openssl"] } | ||||
| actix-webfinger = { version = "0.3.0-alpha.2" } | ||||
| activitystreams = "0.5.0-alpha.6" | ||||
| base64 = "0.12" | ||||
| bb8-postgres = "0.4.0" | ||||
| dotenv = "0.15.0" | ||||
| futures = "0.3.4" | ||||
| http-signature-normalization-actix = { version = "0.3.0-alpha.1", default-features = false, features = ["sha-2"] } | ||||
| log = "0.4" | ||||
| lru = "0.4.3" | ||||
| pretty_env_logger = "0.4.0" | ||||
| rand = "0.7" | ||||
| rsa = "0.2" | ||||
| rsa-magic-public-key = { version = "0.1.0", git = "https://git.asonix.dog/Aardwolf/rsa-magic-public-key" } | ||||
| 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" | ||||
| thiserror = "1.0" | ||||
| tokio = { version = "0.2.13", features = ["sync"] } | ||||
| ttl_cache = "0.5.1" | ||||
| uuid = { version = "0.8", features = ["v4"] } | ||||
| 
 | ||||
| [profile.dev.package.rsa] | ||||
| opt-level = 3 | ||||
|  |  | |||
							
								
								
									
										3
									
								
								migrations/2020-03-16-012053_create-settings/down.sql
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								migrations/2020-03-16-012053_create-settings/down.sql
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| -- This file should undo anything in `up.sql` | ||||
| DROP INDEX settings_key_index; | ||||
| DROP TABLE settings; | ||||
							
								
								
									
										12
									
								
								migrations/2020-03-16-012053_create-settings/up.sql
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								migrations/2020-03-16-012053_create-settings/up.sql
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| -- Your SQL goes here | ||||
| CREATE TABLE settings ( | ||||
|     id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||||
|     key TEXT UNIQUE NOT NULL, | ||||
|     value TEXT NOT NULL, | ||||
|     created_at TIMESTAMP NOT NULL, | ||||
|     updated_at TIMESTAMP | ||||
| ); | ||||
| 
 | ||||
| CREATE INDEX settings_key_index ON settings(key); | ||||
| 
 | ||||
| SELECT diesel_manage_updated_at('settings'); | ||||
							
								
								
									
										26
									
								
								src/apub.rs
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								src/apub.rs
									
										
									
									
									
								
							|  | @ -5,6 +5,23 @@ use activitystreams::{ | |||
| }; | ||||
| use std::collections::HashMap; | ||||
| 
 | ||||
| #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct PublicKey { | ||||
|     pub id: XsdAnyUri, | ||||
|     pub owner: XsdAnyUri, | ||||
|     pub public_key_pem: String, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct PublicKeyExtension<T> { | ||||
|     public_key: PublicKey, | ||||
| 
 | ||||
|     #[serde(flatten)] | ||||
|     extending: T, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PropRefs)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| #[prop_refs(Object)] | ||||
|  | @ -72,6 +89,15 @@ pub struct Endpoints { | |||
|     shared_inbox: Option<XsdAnyUri>, | ||||
| } | ||||
| 
 | ||||
| impl PublicKey { | ||||
|     pub fn extend<T>(self, extending: T) -> PublicKeyExtension<T> { | ||||
|         PublicKeyExtension { | ||||
|             public_key: self, | ||||
|             extending, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ValidObjects { | ||||
|     pub fn id(&self) -> &XsdAnyUri { | ||||
|         match self { | ||||
|  |  | |||
							
								
								
									
										77
									
								
								src/error.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/error.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| use activitystreams::primitives::XsdAnyUriError; | ||||
| use actix_web::{error::ResponseError, http::StatusCode, HttpResponse}; | ||||
| use log::error; | ||||
| use rsa_pem::KeyError; | ||||
| use std::{convert::Infallible, io::Error}; | ||||
| 
 | ||||
| #[derive(Debug, thiserror::Error)] | ||||
| pub enum MyError { | ||||
|     #[error("Couldn't parse key, {0}")] | ||||
|     Key(#[from] KeyError), | ||||
| 
 | ||||
|     #[error("Couldn't parse URI, {0}")] | ||||
|     Uri(#[from] XsdAnyUriError), | ||||
| 
 | ||||
|     #[error("Couldn't perform IO, {0}")] | ||||
|     Io(#[from] Error), | ||||
| 
 | ||||
|     #[error("Couldn't sign string")] | ||||
|     Rsa(rsa::errors::Error), | ||||
| 
 | ||||
|     #[error("Couldn't do the json thing")] | ||||
|     Json(#[from] serde_json::Error), | ||||
| 
 | ||||
|     #[error("Couldn't serialzize the signature header")] | ||||
|     HeaderSerialize(#[from] actix_web::http::header::ToStrError), | ||||
| 
 | ||||
|     #[error("Couldn't parse the signature header")] | ||||
|     HeaderValidation(#[from] actix_web::http::header::InvalidHeaderValue), | ||||
| 
 | ||||
|     #[error("Wrong ActivityPub kind")] | ||||
|     Kind, | ||||
| 
 | ||||
|     #[error("Object has already been relayed")] | ||||
|     Duplicate, | ||||
| 
 | ||||
|     #[error("Actor is blocked")] | ||||
|     Blocked, | ||||
| 
 | ||||
|     #[error("Actor is not whitelisted")] | ||||
|     Whitelist, | ||||
| 
 | ||||
|     #[error("Couldn't send request")] | ||||
|     SendRequest, | ||||
| 
 | ||||
|     #[error("Couldn't receive request response")] | ||||
|     ReceiveResponse, | ||||
| 
 | ||||
|     #[error("Response has invalid status code")] | ||||
|     Status, | ||||
| 
 | ||||
|     #[error("URI is missing domain field")] | ||||
|     Domain, | ||||
| } | ||||
| 
 | ||||
| impl ResponseError for MyError { | ||||
|     fn status_code(&self) -> StatusCode { | ||||
|         StatusCode::INTERNAL_SERVER_ERROR | ||||
|     } | ||||
| 
 | ||||
|     fn error_response(&self) -> HttpResponse { | ||||
|         HttpResponse::InternalServerError() | ||||
|             .header("Content-Type", "application/activity+json") | ||||
|             .json(serde_json::json!({})) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Infallible> for MyError { | ||||
|     fn from(i: Infallible) -> Self { | ||||
|         match i {} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<rsa::errors::Error> for MyError { | ||||
|     fn from(e: rsa::errors::Error) -> Self { | ||||
|         MyError::Rsa(e) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										104
									
								
								src/inbox.rs
									
										
									
									
									
								
							
							
						
						
									
										104
									
								
								src/inbox.rs
									
										
									
									
									
								
							|  | @ -4,20 +4,17 @@ use activitystreams::{ | |||
|     primitives::XsdAnyUri, | ||||
| }; | ||||
| use actix::Addr; | ||||
| use actix_web::{client::Client, http::StatusCode, web, HttpResponse}; | ||||
| use actix_web::{client::Client, web, HttpResponse}; | ||||
| use futures::join; | ||||
| use log::error; | ||||
| 
 | ||||
| use crate::{ | ||||
|     apub::{AcceptedActors, AcceptedObjects, ValidTypes}, | ||||
|     db_actor::{DbActor, DbQuery, Pool}, | ||||
|     error::MyError, | ||||
|     state::{State, UrlKind}, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Clone, Debug, thiserror::Error)] | ||||
| #[error("Something went wrong :(")] | ||||
| pub struct MyError; | ||||
| 
 | ||||
| pub async fn inbox( | ||||
|     db_actor: web::Data<Addr<DbActor>>, | ||||
|     state: web::Data<State>, | ||||
|  | @ -57,7 +54,7 @@ async fn handle_undo( | |||
|     actor: AcceptedActors, | ||||
| ) -> Result<HttpResponse, MyError> { | ||||
|     if !input.object.is_kind("Follow") { | ||||
|         return Err(MyError); | ||||
|         return Err(MyError::Kind); | ||||
|     } | ||||
| 
 | ||||
|     let inbox = actor.inbox().to_owned(); | ||||
|  | @ -99,7 +96,7 @@ async fn handle_undo( | |||
|         let undo2 = undo.clone(); | ||||
|         let client = client.into_inner(); | ||||
|         actix::Arbiter::spawn(async move { | ||||
|             let _ = deliver(&client, actor.id, &undo2).await; | ||||
|             let _ = deliver(&state.into_inner(), &client, actor.id, &undo2).await; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|  | @ -116,7 +113,7 @@ async fn handle_forward( | |||
| 
 | ||||
|     let inboxes = get_inboxes(&state, &actor, &object_id).await?; | ||||
| 
 | ||||
|     deliver_many(client, inboxes, input.clone()); | ||||
|     deliver_many(state, client, inboxes, input.clone()); | ||||
| 
 | ||||
|     Ok(response(input)) | ||||
| } | ||||
|  | @ -130,7 +127,7 @@ async fn handle_relay( | |||
|     let object_id = input.object.id(); | ||||
| 
 | ||||
|     if state.is_cached(object_id).await { | ||||
|         return Err(MyError); | ||||
|         return Err(MyError::Duplicate); | ||||
|     } | ||||
| 
 | ||||
|     let activity_id: XsdAnyUri = state.generate_url(UrlKind::Activity).parse()?; | ||||
|  | @ -149,10 +146,10 @@ async fn handle_relay( | |||
| 
 | ||||
|     let inboxes = get_inboxes(&state, &actor, &object_id).await?; | ||||
| 
 | ||||
|     deliver_many(client, inboxes, announce.clone()); | ||||
| 
 | ||||
|     state.cache(object_id.to_owned(), activity_id).await; | ||||
| 
 | ||||
|     deliver_many(state, client, inboxes, announce.clone()); | ||||
| 
 | ||||
|     Ok(response(announce)) | ||||
| } | ||||
| 
 | ||||
|  | @ -171,12 +168,12 @@ async fn handle_follow( | |||
| 
 | ||||
|     if is_blocked { | ||||
|         error!("Follow from blocked listener, {}", actor.id); | ||||
|         return Err(MyError); | ||||
|         return Err(MyError::Blocked); | ||||
|     } | ||||
| 
 | ||||
|     if !is_whitelisted { | ||||
|         error!("Follow from non-whitelisted listener, {}", actor.id); | ||||
|         return Err(MyError); | ||||
|         return Err(MyError::Whitelist); | ||||
|     } | ||||
| 
 | ||||
|     if !is_listener { | ||||
|  | @ -220,7 +217,7 @@ async fn handle_follow( | |||
|     let client = client.into_inner(); | ||||
|     let accept2 = accept.clone(); | ||||
|     actix::Arbiter::spawn(async move { | ||||
|         let _ = deliver(&client, actor_inbox, &accept2).await; | ||||
|         let _ = deliver(&state.into_inner(), &client, actor_inbox, &accept2).await; | ||||
|     }); | ||||
| 
 | ||||
|     Ok(response(accept)) | ||||
|  | @ -242,13 +239,13 @@ async fn fetch_actor( | |||
|         .await | ||||
|         .map_err(|e| { | ||||
|             error!("Couldn't send request to {} for actor, {}", actor_id, e); | ||||
|             MyError | ||||
|             MyError::SendRequest | ||||
|         })? | ||||
|         .json() | ||||
|         .await | ||||
|         .map_err(|e| { | ||||
|             error!("Coudn't fetch actor from {}, {}", actor_id, e); | ||||
|             MyError | ||||
|             MyError::ReceiveResponse | ||||
|         })?; | ||||
| 
 | ||||
|     state.cache_actor(actor_id.to_owned(), actor.clone()).await; | ||||
|  | @ -256,20 +253,24 @@ async fn fetch_actor( | |||
|     Ok(actor) | ||||
| } | ||||
| 
 | ||||
| fn deliver_many<T>(client: web::Data<Client>, inboxes: Vec<XsdAnyUri>, item: T) | ||||
| where | ||||
| fn deliver_many<T>( | ||||
|     state: web::Data<State>, | ||||
|     client: web::Data<Client>, | ||||
|     inboxes: Vec<XsdAnyUri>, | ||||
|     item: T, | ||||
| ) where | ||||
|     T: serde::ser::Serialize + 'static, | ||||
| { | ||||
|     let client = client.into_inner(); | ||||
|     let state = state.into_inner(); | ||||
| 
 | ||||
|     actix::Arbiter::spawn(async move { | ||||
|         use futures::stream::StreamExt; | ||||
| 
 | ||||
|         let client = client.clone(); | ||||
|         let mut unordered = futures::stream::FuturesUnordered::new(); | ||||
| 
 | ||||
|         for inbox in inboxes { | ||||
|             unordered.push(deliver(&client, inbox, &item)); | ||||
|             unordered.push(deliver(&state, &client, inbox, &item)); | ||||
|         } | ||||
| 
 | ||||
|         while let Some(_) = unordered.next().await {} | ||||
|  | @ -277,6 +278,7 @@ where | |||
| } | ||||
| 
 | ||||
| async fn deliver<T>( | ||||
|     state: &std::sync::Arc<State>, | ||||
|     client: &std::sync::Arc<Client>, | ||||
|     inbox: XsdAnyUri, | ||||
|     item: &T, | ||||
|  | @ -284,20 +286,38 @@ async fn deliver<T>( | |||
| where | ||||
|     T: serde::ser::Serialize, | ||||
| { | ||||
|     use http_signature_normalization_actix::prelude::*; | ||||
|     use sha2::{Digest, Sha256}; | ||||
| 
 | ||||
|     let config = Config::default(); | ||||
|     let mut digest = Sha256::new(); | ||||
| 
 | ||||
|     let key_id = state.generate_url(UrlKind::Actor); | ||||
| 
 | ||||
|     let item_string = serde_json::to_string(item)?; | ||||
| 
 | ||||
|     let res = client | ||||
|         .post(inbox.as_str()) | ||||
|         .header("Accept", "application/activity+json") | ||||
|         .header("Content-Type", "application/activity+json") | ||||
|         .send_json(item) | ||||
|         .header("User-Agent", "Aode Relay v0.1.0") | ||||
|         .signature_with_digest( | ||||
|             &config, | ||||
|             &key_id, | ||||
|             &mut digest, | ||||
|             item_string, | ||||
|             |signing_string| state.sign(signing_string.as_bytes()), | ||||
|         )? | ||||
|         .send() | ||||
|         .await | ||||
|         .map_err(|e| { | ||||
|             error!("Couldn't send deliver request to {}, {}", inbox, e); | ||||
|             MyError | ||||
|             MyError::SendRequest | ||||
|         })?; | ||||
| 
 | ||||
|     if !res.status().is_success() { | ||||
|         error!("Invalid response status from {}, {}", inbox, res.status()); | ||||
|         return Err(MyError); | ||||
|         return Err(MyError::Status); | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
|  | @ -308,41 +328,13 @@ async fn get_inboxes( | |||
|     actor: &AcceptedActors, | ||||
|     object_id: &XsdAnyUri, | ||||
| ) -> Result<Vec<XsdAnyUri>, MyError> { | ||||
|     let domain = object_id.as_url().host().ok_or(MyError)?.to_string(); | ||||
|     let domain = object_id | ||||
|         .as_url() | ||||
|         .host() | ||||
|         .ok_or(MyError::Domain)? | ||||
|         .to_string(); | ||||
| 
 | ||||
|     let inbox = actor.inbox(); | ||||
| 
 | ||||
|     Ok(state.listeners_without(&inbox, &domain).await) | ||||
| } | ||||
| 
 | ||||
| impl actix_web::error::ResponseError for MyError { | ||||
|     fn status_code(&self) -> StatusCode { | ||||
|         StatusCode::INTERNAL_SERVER_ERROR | ||||
|     } | ||||
| 
 | ||||
|     fn error_response(&self) -> HttpResponse { | ||||
|         HttpResponse::InternalServerError() | ||||
|             .header("Content-Type", "application/activity+json") | ||||
|             .json(serde_json::json!({})) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<std::convert::Infallible> for MyError { | ||||
|     fn from(_: std::convert::Infallible) -> Self { | ||||
|         MyError | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<activitystreams::primitives::XsdAnyUriError> for MyError { | ||||
|     fn from(_: activitystreams::primitives::XsdAnyUriError) -> Self { | ||||
|         error!("Error parsing URI"); | ||||
|         MyError | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<std::io::Error> for MyError { | ||||
|     fn from(e: std::io::Error) -> Self { | ||||
|         error!("JSON Error, {}", e); | ||||
|         MyError | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										15
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								src/main.rs
									
										
									
									
									
								
							|  | @ -2,16 +2,20 @@ | |||
| use activitystreams::{actor::apub::Application, context, endpoint::EndpointProperties}; | ||||
| use actix_web::{client::Client, middleware::Logger, web, App, HttpServer, Responder}; | ||||
| use bb8_postgres::tokio_postgres; | ||||
| use rsa_pem::KeyExt; | ||||
| 
 | ||||
| mod apub; | ||||
| mod db_actor; | ||||
| mod error; | ||||
| mod inbox; | ||||
| mod label; | ||||
| mod state; | ||||
| mod webfinger; | ||||
| 
 | ||||
| use self::{ | ||||
|     apub::PublicKey, | ||||
|     db_actor::DbActor, | ||||
|     inbox::MyError, | ||||
|     error::MyError, | ||||
|     label::ArbiterLabelFactory, | ||||
|     state::{State, UrlKind}, | ||||
| }; | ||||
|  | @ -42,7 +46,13 @@ async fn actor_route(state: web::Data<State>) -> Result<impl Responder, MyError> | |||
|         .set_inbox(state.generate_url(UrlKind::Inbox))? | ||||
|         .set_endpoints(endpoint)?; | ||||
| 
 | ||||
|     Ok(inbox::response(application)) | ||||
|     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(inbox::response(public_key.extend(application))) | ||||
| } | ||||
| 
 | ||||
| #[actix_rt::main] | ||||
|  | @ -81,6 +91,7 @@ async fn main() -> Result<(), anyhow::Error> { | |||
|             .service(web::resource("/").route(web::get().to(index))) | ||||
|             .service(web::resource("/inbox").route(web::post().to(inbox::inbox))) | ||||
|             .service(web::resource("/actor").route(web::get().to(actor_route))) | ||||
|             .service(actix_webfinger::resource::<_, webfinger::RelayResolver>()) | ||||
|     }) | ||||
|     .bind("127.0.0.1:8080")? | ||||
|     .run() | ||||
|  |  | |||
|  | @ -16,6 +16,16 @@ table! { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| table! { | ||||
|     settings (id) { | ||||
|         id -> Uuid, | ||||
|         key -> Text, | ||||
|         value -> Text, | ||||
|         created_at -> Timestamp, | ||||
|         updated_at -> Nullable<Timestamp>, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| table! { | ||||
|     whitelists (id) { | ||||
|         id -> Uuid, | ||||
|  | @ -28,5 +38,6 @@ table! { | |||
| allow_tables_to_appear_in_same_query!( | ||||
|     blocks, | ||||
|     listeners, | ||||
|     settings, | ||||
|     whitelists, | ||||
| ); | ||||
|  |  | |||
							
								
								
									
										121
									
								
								src/state.rs
									
										
									
									
									
								
							
							
						
						
									
										121
									
								
								src/state.rs
									
										
									
									
									
								
							|  | @ -2,7 +2,11 @@ use activitystreams::primitives::XsdAnyUri; | |||
| use anyhow::Error; | ||||
| use bb8_postgres::tokio_postgres::{row::Row, Client}; | ||||
| use futures::try_join; | ||||
| use log::{error, info}; | ||||
| use lru::LruCache; | ||||
| use rand::thread_rng; | ||||
| use rsa::{RSAPrivateKey, RSAPublicKey}; | ||||
| use rsa_pem::KeyExt; | ||||
| use std::{collections::HashSet, sync::Arc}; | ||||
| use tokio::sync::RwLock; | ||||
| use ttl_cache::TtlCache; | ||||
|  | @ -12,9 +16,7 @@ use crate::{apub::AcceptedActors, db_actor::Pool}; | |||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct State { | ||||
|     use_https: bool, | ||||
|     hostname: String, | ||||
|     whitelist_enabled: bool, | ||||
|     pub settings: Settings, | ||||
|     actor_cache: Arc<RwLock<TtlCache<XsdAnyUri, AcceptedActors>>>, | ||||
|     actor_id_cache: Arc<RwLock<LruCache<XsdAnyUri, XsdAnyUri>>>, | ||||
|     blocks: Arc<RwLock<HashSet<String>>>, | ||||
|  | @ -22,20 +24,73 @@ 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, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, thiserror::Error)] | ||||
| #[error("No host present in URI")] | ||||
| pub struct HostError; | ||||
| 
 | ||||
| impl State { | ||||
|     pub fn generate_url(&self, kind: UrlKind) -> String { | ||||
| #[derive(Clone, Debug, thiserror::Error)] | ||||
| #[error("Error generating RSA key")] | ||||
| pub struct RsaError; | ||||
| 
 | ||||
| impl Settings { | ||||
|     async fn hydrate( | ||||
|         client: &Client, | ||||
|         use_https: bool, | ||||
|         whitelist_enabled: bool, | ||||
|         hostname: String, | ||||
|     ) -> Result<Self, Error> { | ||||
|         info!("SELECT value FROM settings WHERE key = 'private_key'"); | ||||
|         let rows = client | ||||
|             .query("SELECT value FROM settings WHERE key = 'private_key'", &[]) | ||||
|             .await?; | ||||
| 
 | ||||
|         let private_key = if let Some(row) = rows.into_iter().next() { | ||||
|             let key_str: String = row.get(0); | ||||
|             KeyExt::from_pem_pkcs8(&key_str)? | ||||
|         } else { | ||||
|             info!("Generating new keys"); | ||||
|             let mut rng = thread_rng(); | ||||
|             let key = RSAPrivateKey::new(&mut rng, 4096).map_err(|e| { | ||||
|                 error!("Error generating RSA key, {}", e); | ||||
|                 RsaError | ||||
|             })?; | ||||
|             let pem_pkcs8 = key.to_pem_pkcs8()?; | ||||
| 
 | ||||
|             info!("INSERT INTO settings (key, value, created_at) VALUES ('private_key', $1::TEXT, 'now');"); | ||||
|             client.execute("INSERT INTO settings (key, value, created_at) VALUES ('private_key', $1::TEXT, 'now');", &[&pem_pkcs8]).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 { | ||||
|  | @ -46,13 +101,40 @@ impl State { | |||
|             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), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn generate_resource(&self) -> String { | ||||
|         format!("relay@{}", self.hostname) | ||||
|     } | ||||
| 
 | ||||
|     fn sign(&self, bytes: &[u8]) -> Result<String, crate::error::MyError> { | ||||
|         use rsa::{hash::Hashes, padding::PaddingScheme}; | ||||
|         let bytes = | ||||
|             self.private_key | ||||
|                 .sign(PaddingScheme::PKCS1v15, Some(&Hashes::SHA2_256), bytes)?; | ||||
|         Ok(base64::encode_config(bytes, base64::URL_SAFE)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl State { | ||||
|     pub fn generate_url(&self, kind: UrlKind) -> String { | ||||
|         self.settings.generate_url(kind) | ||||
|     } | ||||
| 
 | ||||
|     pub fn generate_resource(&self) -> String { | ||||
|         self.settings.generate_resource() | ||||
|     } | ||||
| 
 | ||||
|     pub fn sign(&self, bytes: &[u8]) -> Result<String, crate::error::MyError> { | ||||
|         self.settings.sign(bytes) | ||||
|     } | ||||
| 
 | ||||
|     pub async fn remove_listener(&self, client: &Client, inbox: &XsdAnyUri) -> Result<(), Error> { | ||||
|         let hs = self.listeners.clone(); | ||||
| 
 | ||||
|         log::info!("DELETE FROM listeners WHERE actor_id = {};", inbox.as_str()); | ||||
|         info!("DELETE FROM listeners WHERE actor_id = {};", inbox.as_str()); | ||||
|         client | ||||
|             .execute( | ||||
|                 "DELETE FROM listeners WHERE actor_id = $1::TEXT;", | ||||
|  | @ -86,7 +168,7 @@ impl State { | |||
|     } | ||||
| 
 | ||||
|     pub async fn is_whitelisted(&self, actor_id: &XsdAnyUri) -> bool { | ||||
|         if !self.whitelist_enabled { | ||||
|         if !self.settings.whitelist_enabled { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|  | @ -155,7 +237,7 @@ impl State { | |||
|             return Err(HostError.into()); | ||||
|         }; | ||||
| 
 | ||||
|         log::info!( | ||||
|         info!( | ||||
|             "INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]", | ||||
|             host.to_string() | ||||
|         ); | ||||
|  | @ -181,7 +263,7 @@ impl State { | |||
|             return Err(HostError.into()); | ||||
|         }; | ||||
| 
 | ||||
|         log::info!( | ||||
|         info!( | ||||
|             "INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]", | ||||
|             host.to_string() | ||||
|         ); | ||||
|  | @ -201,7 +283,7 @@ impl State { | |||
|     pub async fn add_listener(&self, client: &Client, listener: XsdAnyUri) -> Result<(), Error> { | ||||
|         let listeners = self.listeners.clone(); | ||||
| 
 | ||||
|         log::info!( | ||||
|         info!( | ||||
|             "INSERT INTO listeners (actor_id, created_at) VALUES ($1::TEXT, 'now'); [{}]", | ||||
|             listener.as_str(), | ||||
|         ); | ||||
|  | @ -226,6 +308,7 @@ impl State { | |||
|     ) -> Result<Self, Error> { | ||||
|         let pool1 = pool.clone(); | ||||
|         let pool2 = pool.clone(); | ||||
|         let pool3 = pool.clone(); | ||||
| 
 | ||||
|         let f1 = async move { | ||||
|             let conn = pool.get().await?; | ||||
|  | @ -245,12 +328,16 @@ impl State { | |||
|             hydrate_listeners(&conn).await | ||||
|         }; | ||||
| 
 | ||||
|         let (blocks, whitelists, listeners) = try_join!(f1, f2, f3)?; | ||||
|         let f4 = async move { | ||||
|             let conn = pool3.get().await?; | ||||
| 
 | ||||
|             Settings::hydrate(&conn, use_https, whitelist_enabled, hostname).await | ||||
|         }; | ||||
| 
 | ||||
|         let (blocks, whitelists, listeners, settings) = try_join!(f1, f2, f3, f4)?; | ||||
| 
 | ||||
|         Ok(State { | ||||
|             use_https, | ||||
|             whitelist_enabled, | ||||
|             hostname, | ||||
|             settings, | ||||
|             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)), | ||||
|  | @ -261,14 +348,14 @@ impl State { | |||
| } | ||||
| 
 | ||||
| pub async fn hydrate_blocks(client: &Client) -> Result<HashSet<String>, Error> { | ||||
|     log::info!("SELECT domain_name FROM blocks"); | ||||
|     info!("SELECT domain_name FROM blocks"); | ||||
|     let rows = client.query("SELECT domain_name FROM blocks", &[]).await?; | ||||
| 
 | ||||
|     parse_rows(rows) | ||||
| } | ||||
| 
 | ||||
| pub async fn hydrate_whitelists(client: &Client) -> Result<HashSet<String>, Error> { | ||||
|     log::info!("SELECT domain_name FROM whitelists"); | ||||
|     info!("SELECT domain_name FROM whitelists"); | ||||
|     let rows = client | ||||
|         .query("SELECT domain_name FROM whitelists", &[]) | ||||
|         .await?; | ||||
|  | @ -277,7 +364,7 @@ pub async fn hydrate_whitelists(client: &Client) -> Result<HashSet<String>, Erro | |||
| } | ||||
| 
 | ||||
| pub async fn hydrate_listeners(client: &Client) -> Result<HashSet<XsdAnyUri>, Error> { | ||||
|     log::info!("SELECT actor_id FROM listeners"); | ||||
|     info!("SELECT actor_id FROM listeners"); | ||||
|     let rows = client.query("SELECT actor_id FROM listeners", &[]).await?; | ||||
| 
 | ||||
|     parse_rows(rows) | ||||
|  |  | |||
							
								
								
									
										52
									
								
								src/webfinger.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/webfinger.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | |||
| use crate::state::{State, UrlKind}; | ||||
| use activitystreams::context; | ||||
| use actix_web::web::Data; | ||||
| use actix_webfinger::{Link, Resolver, Webfinger}; | ||||
| use rsa_magic_public_key::AsMagicPublicKey; | ||||
| use std::{future::Future, pin::Pin}; | ||||
| 
 | ||||
| pub struct RelayResolver; | ||||
| 
 | ||||
| #[derive(Clone, Debug, thiserror::Error)] | ||||
| #[error("Error resolving webfinger data")] | ||||
| pub struct RelayError; | ||||
| 
 | ||||
| impl Resolver<Data<State>> for RelayResolver { | ||||
|     type Error = RelayError; | ||||
| 
 | ||||
|     fn find( | ||||
|         account: &str, | ||||
|         domain: &str, | ||||
|         state: Data<State>, | ||||
|     ) -> 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 { | ||||
|                 return Ok(None); | ||||
|             } | ||||
| 
 | ||||
|             if account != "relay" { | ||||
|                 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()) | ||||
|                 .add_link(Link { | ||||
|                     rel: "self".to_owned(), | ||||
|                     href: Some(state.generate_url(UrlKind::Actor)), | ||||
|                     template: None, | ||||
|                     kind: Some(format!("application/ld+json; profile=\"{}\"", context())), | ||||
|                 }); | ||||
| 
 | ||||
|             Ok(Some(wf)) | ||||
|         }; | ||||
| 
 | ||||
|         Box::pin(fut) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl actix_web::error::ResponseError for RelayError {} | ||||
		Loading…
	
	Add table
		
		Reference in a new issue