From 9d4973829bb2ff670401ab0d8b7810db103df7b9 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 18 Sep 2020 16:37:46 +0200 Subject: [PATCH] Add integration test to ensure that signatures are verified --- lemmy_utils/src/lib.rs | 2 +- src/apub/activity_queue.rs | 1 - src/lib.rs | 14 ++-- src/main.rs | 2 +- src/websocket/chat_server.rs | 7 +- test.sh | 2 +- tests/integration_test.rs | 126 +++++++++++++++++++++++++++++++++++ 7 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 tests/integration_test.rs diff --git a/lemmy_utils/src/lib.rs b/lemmy_utils/src/lib.rs index 9cc1fe02..247705e4 100644 --- a/lemmy_utils/src/lib.rs +++ b/lemmy_utils/src/lib.rs @@ -57,7 +57,7 @@ impl APIError { #[derive(Debug)] pub struct LemmyError { - inner: anyhow::Error, + pub inner: anyhow::Error, } impl From for LemmyError diff --git a/src/apub/activity_queue.rs b/src/apub/activity_queue.rs index 008846bf..a48cf5bd 100644 --- a/src/apub/activity_queue.rs +++ b/src/apub/activity_queue.rs @@ -79,7 +79,6 @@ impl ActixJob for SendActivityTask { .post(to_url.as_str()) .header("Content-Type", "application/json"); - // TODO: i believe we have to do the signing in here because it is only valid for a few seconds let signed = sign( request, self.activity.clone(), diff --git a/src/lib.rs b/src/lib.rs index 2bab3552..5d5f00b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ pub struct LemmyContext { } impl LemmyContext { - pub fn create( + pub fn new( pool: DbPool, chat_server: Addr, client: Client, @@ -79,12 +79,12 @@ impl LemmyContext { impl Clone for LemmyContext { fn clone(&self) -> Self { - LemmyContext { - pool: self.pool.clone(), - chat_server: self.chat_server.clone(), - client: self.client.clone(), - activity_queue: self.activity_queue.clone(), - } + LemmyContext::new( + self.pool.clone(), + self.chat_server.clone(), + self.client.clone(), + self.activity_queue.clone(), + ) } } diff --git a/src/main.rs b/src/main.rs index 800196cf..d14e006e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,7 +84,7 @@ async fn main() -> Result<(), LemmyError> { // Create Http server with websocket support HttpServer::new(move || { - let context = LemmyContext::create( + let context = LemmyContext::new( pool.clone(), chat_server.to_owned(), Client::default(), diff --git a/src/websocket/chat_server.rs b/src/websocket/chat_server.rs index 0c4dfb0b..611f44f9 100644 --- a/src/websocket/chat_server.rs +++ b/src/websocket/chat_server.rs @@ -361,12 +361,7 @@ impl ChatServer { let user_operation: UserOperation = UserOperation::from_str(&op)?; - let context = LemmyContext { - pool, - chat_server: addr, - client, - activity_queue, - }; + let context = LemmyContext::new(pool, addr, client, activity_queue); let args = Args { context, rate_limiter, diff --git a/test.sh b/test.sh index beb499bd..801e95a4 100755 --- a/test.sh +++ b/test.sh @@ -2,4 +2,4 @@ export DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy diesel migration run export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy -RUST_TEST_THREADS=1 cargo test --workspace --no-fail-fast +RUST_TEST_THREADS=1 RUST_BACKTRACE=1 cargo test -j8 --no-fail-fast -- --nocapture diff --git a/tests/integration_test.rs b/tests/integration_test.rs new file mode 100644 index 00000000..a127ad42 --- /dev/null +++ b/tests/integration_test.rs @@ -0,0 +1,126 @@ +extern crate lemmy_server; + +use activitystreams::{ + activity::{kind::CreateType, ActorAndObject}, + base::{BaseExt, ExtendsExt}, + object::{Note, ObjectExt}, +}; +use actix::prelude::*; +use actix_web::{test::TestRequest, web}; +use chrono::Utc; +use diesel::{ + r2d2::{ConnectionManager, Pool}, + PgConnection, +}; +use http_signature_normalization_actix::PrepareVerifyError; +use lemmy_db::{ + user::{User_, *}, + Crud, + ListingType, + SortType, +}; +use lemmy_rate_limit::{rate_limiter::RateLimiter, RateLimit}; +use lemmy_server::{ + apub::{ + activity_queue::create_activity_queue, + inbox::shared_inbox::{shared_inbox, ValidTypes}, + }, + websocket::chat_server::ChatServer, + LemmyContext, +}; +use lemmy_utils::{apub::generate_actor_keypair, settings::Settings}; +use reqwest::Client; +use std::sync::Arc; +use tokio::sync::Mutex; +use url::Url; + +fn create_context() -> LemmyContext { + let settings = Settings::get(); + let db_url = settings.get_database_url(); + let manager = ConnectionManager::::new(&db_url); + let pool = Pool::builder() + .max_size(settings.database.pool_size) + .build(manager) + .unwrap(); + let rate_limiter = RateLimit { + rate_limiter: Arc::new(Mutex::new(RateLimiter::default())), + }; + let activity_queue = create_activity_queue(); + let chat_server = ChatServer::startup( + pool.clone(), + rate_limiter.clone(), + Client::default(), + activity_queue.clone(), + ) + .start(); + LemmyContext::new( + pool, + chat_server, + Client::default(), + create_activity_queue(), + ) +} + +fn create_user(conn: &PgConnection) -> User_ { + let user_keypair = generate_actor_keypair().unwrap(); + let new_user = UserForm { + name: "integration_user_1".into(), + preferred_username: None, + password_encrypted: "nope".into(), + email: None, + matrix_user_id: None, + avatar: None, + banner: None, + admin: false, + banned: false, + updated: None, + show_nsfw: false, + theme: "darkly".into(), + default_sort_type: SortType::Hot as i16, + default_listing_type: ListingType::Subscribed as i16, + lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, + actor_id: Some("http://localhost:8536/u/integration_user_1".to_string()), + bio: None, + local: true, + private_key: Some(user_keypair.private_key), + public_key: Some(user_keypair.public_key), + last_refreshed_at: None, + }; + + User_::create(&conn, &new_user).unwrap() +} + +fn create_activity(user_id: String) -> web::Json> { + let mut activity = + ActorAndObject::::new(user_id, Note::new().into_any_base().unwrap()); + activity + .set_id(Url::parse("http://localhost:8536/create/1").unwrap()) + .set_many_ccs(vec![Url::parse("http://localhost:8536/c/main").unwrap()]); + let activity = serde_json::to_value(&activity).unwrap(); + let activity: ActorAndObject = serde_json::from_value(activity).unwrap(); + web::Json(activity) +} + +#[actix_rt::test] +async fn test_expired_signature() { + let time1 = Utc::now().timestamp(); + let time2 = Utc::now().timestamp(); + let signature = format!( + r#"keyId="my-key-id",algorithm="hs2019",created="{}",expires="{}",headers="(request-target) (created) (expires) date content-type",signature="blah blah blah""#, + time1, time2 + ); + let request = TestRequest::post() + .uri("http://localhost:8536/inbox") + .header("Signature", signature) + .to_http_request(); + let context = create_context(); + let user = create_user(&context.pool().get().unwrap()); + let activity = create_activity(user.actor_id); + let response = shared_inbox(request, activity, web::Data::new(context)).await; + assert_eq!( + format!("{}", response.err().unwrap()), + format!("{}", PrepareVerifyError::Expired) + ); +} -- 2.44.1