From 7fe7062c471d29bfe199887bac318a6f127ae44f Mon Sep 17 00:00:00 2001
From: layla <layla@chapo.dev>
Date: Thu, 11 Nov 2021 20:40:25 +0000
Subject: [PATCH] Implement rate limits on comments

---
 config/defaults.hjson                       |  4 ++++
 crates/utils/src/rate_limit/mod.rs          | 13 +++++++++++++
 crates/utils/src/rate_limit/rate_limiter.rs |  1 +
 crates/utils/src/settings/structs.rs        |  6 ++++++
 crates/websocket/src/chat_server.rs         |  1 +
 docker/federation/lemmy_alpha.hjson         |  2 ++
 docker/federation/lemmy_beta.hjson          |  2 ++
 docker/federation/lemmy_delta.hjson         |  2 ++
 docker/federation/lemmy_epsilon.hjson       |  2 ++
 docker/federation/lemmy_gamma.hjson         |  2 ++
 src/api_routes.rs                           |  8 +++++++-
 11 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/config/defaults.hjson b/config/defaults.hjson
index ff6df36d..003adf11 100644
--- a/config/defaults.hjson
+++ b/config/defaults.hjson
@@ -32,6 +32,10 @@
     image: 6
     # Interval length for image uploads, in seconds
     image_per_second: 3600
+    # Maximum number of comments created in interval
+    comment: 6
+    # Interval length for comment limit, in seconds
+    comment_per_second: 600
   }
   # Settings related to activitypub federation
   federation: {
diff --git a/crates/utils/src/rate_limit/mod.rs b/crates/utils/src/rate_limit/mod.rs
index c1a4627c..d56dc0c5 100644
--- a/crates/utils/src/rate_limit/mod.rs
+++ b/crates/utils/src/rate_limit/mod.rs
@@ -49,6 +49,10 @@ impl RateLimit {
     self.kind(RateLimitType::Image)
   }
 
+  pub fn comment(&self) -> RateLimited {
+    self.kind(RateLimitType::Comment)
+  }
+
   fn kind(&self, type_: RateLimitType) -> RateLimited {
     RateLimited {
       rate_limiter: self.rate_limiter.clone(),
@@ -115,6 +119,15 @@ impl RateLimited {
             false,
           )?;
         }
+        RateLimitType::Comment => {
+          limiter.check_rate_limit_full(
+            self.type_,
+            &ip_addr,
+            rate_limit.comment,
+            rate_limit.comment_per_second,
+            false,
+          )?;
+        }
       };
     }
 
diff --git a/crates/utils/src/rate_limit/rate_limiter.rs b/crates/utils/src/rate_limit/rate_limiter.rs
index 46b6b0c7..352d5e66 100644
--- a/crates/utils/src/rate_limit/rate_limiter.rs
+++ b/crates/utils/src/rate_limit/rate_limiter.rs
@@ -15,6 +15,7 @@ pub(crate) enum RateLimitType {
   Register,
   Post,
   Image,
+  Comment,
 }
 
 /// Rate limiting based on rate type and IP addr
diff --git a/crates/utils/src/settings/structs.rs b/crates/utils/src/settings/structs.rs
index 30003081..1b8ac812 100644
--- a/crates/utils/src/settings/structs.rs
+++ b/crates/utils/src/settings/structs.rs
@@ -149,6 +149,12 @@ pub struct RateLimitConfig {
   /// Interval length for image uploads, in seconds
   #[default(3600)]
   pub image_per_second: i32,
+  /// Maximum number of comments created in interval
+  #[default(6)]
+  pub comment: i32,
+  /// Interval length for comment limit, in seconds
+  #[default(600)]
+  pub comment_per_second: i32,
 }
 
 #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
diff --git a/crates/websocket/src/chat_server.rs b/crates/websocket/src/chat_server.rs
index 3e17b626..9fa258ff 100644
--- a/crates/websocket/src/chat_server.rs
+++ b/crates/websocket/src/chat_server.rs
@@ -485,6 +485,7 @@ impl ChatServer {
           UserOperationCrud::Register => rate_limiter.register().wrap(ip, fut).await,
           UserOperationCrud::CreatePost => rate_limiter.post().wrap(ip, fut).await,
           UserOperationCrud::CreateCommunity => rate_limiter.register().wrap(ip, fut).await,
+          UserOperationCrud::CreateComment => rate_limiter.comment().wrap(ip, fut).await,
           _ => rate_limiter.message().wrap(ip, fut).await,
         }
       } else {
diff --git a/docker/federation/lemmy_alpha.hjson b/docker/federation/lemmy_alpha.hjson
index 8c69a4c6..6c0c54df 100644
--- a/docker/federation/lemmy_alpha.hjson
+++ b/docker/federation/lemmy_alpha.hjson
@@ -33,5 +33,7 @@
     register_per_second: 3600
     image: 6
     image_per_second: 3600
+    comment: 99999
+    comment_per_second: 600
   }
 }
diff --git a/docker/federation/lemmy_beta.hjson b/docker/federation/lemmy_beta.hjson
index efd9458c..b630daca 100644
--- a/docker/federation/lemmy_beta.hjson
+++ b/docker/federation/lemmy_beta.hjson
@@ -32,5 +32,7 @@
     register_per_second: 3600
     image: 6
     image_per_second: 3600
+    comment: 99999
+    comment_per_second: 600
   }
 }
diff --git a/docker/federation/lemmy_delta.hjson b/docker/federation/lemmy_delta.hjson
index 75f3f916..8d400fc0 100644
--- a/docker/federation/lemmy_delta.hjson
+++ b/docker/federation/lemmy_delta.hjson
@@ -32,5 +32,7 @@
     register_per_second: 3600
     image: 6
     image_per_second: 3600
+    comment: 99999
+    comment_per_second: 600
   }
 }
diff --git a/docker/federation/lemmy_epsilon.hjson b/docker/federation/lemmy_epsilon.hjson
index 1b2cbd34..78b1a687 100644
--- a/docker/federation/lemmy_epsilon.hjson
+++ b/docker/federation/lemmy_epsilon.hjson
@@ -32,5 +32,7 @@
     register_per_second: 3600
     image: 6
     image_per_second: 3600
+    comment: 99999
+    comment_per_second: 600
   }
 }
diff --git a/docker/federation/lemmy_gamma.hjson b/docker/federation/lemmy_gamma.hjson
index 48b6c1c5..8d3f097a 100644
--- a/docker/federation/lemmy_gamma.hjson
+++ b/docker/federation/lemmy_gamma.hjson
@@ -32,5 +32,7 @@
     register_per_second: 3600
     image: 6
     image_per_second: 3600
+    comment: 99999
+    comment_per_second: 600
   }
 }
diff --git a/src/api_routes.rs b/src/api_routes.rs
index 80d507ef..0349f518 100644
--- a/src/api_routes.rs
+++ b/src/api_routes.rs
@@ -101,10 +101,16 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
           ),
       )
       // Comment
+      .service(
+        // Handle POST to /comment separately to add the comment() rate limitter
+        web::resource("/comment")
+          .guard(guard::Post())
+          .wrap(rate_limit.comment())
+          .route(web::post().to(route_post_crud::<CreateComment>)),
+      )
       .service(
         web::scope("/comment")
           .wrap(rate_limit.message())
-          .route("", web::post().to(route_post_crud::<CreateComment>))
           .route("", web::put().to(route_post_crud::<EditComment>))
           .route("/delete", web::post().to(route_post_crud::<DeleteComment>))
           .route("/remove", web::post().to(route_post_crud::<RemoveComment>))
-- 
2.44.1