]> Untitled Git - lemmy.git/commitdiff
Add integration test to ensure that signatures are verified
authorFelix Ableitner <me@nutomic.com>
Fri, 18 Sep 2020 14:37:46 +0000 (16:37 +0200)
committerFelix Ableitner <me@nutomic.com>
Fri, 18 Sep 2020 14:37:46 +0000 (16:37 +0200)
lemmy_utils/src/lib.rs
src/apub/activity_queue.rs
src/lib.rs
src/main.rs
src/websocket/chat_server.rs
test.sh
tests/integration_test.rs [new file with mode: 0644]

index 9cc1fe025c498550881aa4c636327c2c116ab067..247705e420e2807436b985307aade45fc027bf19 100644 (file)
@@ -57,7 +57,7 @@ impl APIError {
 
 #[derive(Debug)]
 pub struct LemmyError {
-  inner: anyhow::Error,
+  pub inner: anyhow::Error,
 }
 
 impl<T> From<T> for LemmyError
index 008846bf357bb5ada84bfe73088fdf63d1ce5b2a..a48cf5bd2184abc2dc4a7695b6913fee4cc3a6fb 100644 (file)
@@ -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(),
index 2bab3552aed4c1bf25f49dfc0f4c233d2afc450a..5d5f00b0de01960d0181212995656c5ca2bfffce 100644 (file)
@@ -50,7 +50,7 @@ pub struct LemmyContext {
 }
 
 impl LemmyContext {
-  pub fn create(
+  pub fn new(
     pool: DbPool,
     chat_server: Addr<ChatServer>,
     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(),
+    )
   }
 }
 
index 800196cfe9204260471f0b1f1e0db260b27a4da5..d14e006e2fb75be8fedc938804d68dd58e1b7a29 100644 (file)
@@ -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(),
index 0c4dfb0ba1e5075cec35d9062cf17dd64e9c2fdb..611f44f9715ba6caa12e6b92e4c6d218787b8d7a 100644 (file)
@@ -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 beb499bdffb2a68b2755356b79082cb860715b39..801e95a4194f050fc46d71de7f2b9dfa2b758dcb 100755 (executable)
--- 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 (file)
index 0000000..a127ad4
--- /dev/null
@@ -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::<PgConnection>::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<ActorAndObject<ValidTypes>> {
+  let mut activity =
+    ActorAndObject::<CreateType>::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<ValidTypes> = 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)
+  );
+}