Browse Source

Merge pull request 'fix/better-ratelimits' (#316) from fix/better-ratelimits into main

Reviewed-on: https://git.chapo.chat/hexbear-collective/lemmy-hexbear/pulls/316
main v0.7.60.hex
DashEightMate 6 months ago
parent
commit
50e94f0422
  1. 24
      server/config/config.hjson
  2. 6
      server/config/defaults.hjson
  3. 2
      server/lemmy_api_structs/src/community.rs
  4. 2
      server/lemmy_api_structs/src/lib.rs
  5. 19
      server/lemmy_api_structs/src/site.rs
  6. 4
      server/lemmy_db/src/activity.rs
  7. 35
      server/lemmy_db/src/comment.rs
  8. 17
      server/lemmy_db/src/comment_view.rs
  9. 7
      server/lemmy_db/src/community.rs
  10. 2
      server/lemmy_db/src/lib.rs
  11. 21
      server/lemmy_db/src/moderator.rs
  12. 7
      server/lemmy_db/src/password_reset_request.rs
  13. 36
      server/lemmy_db/src/post.rs
  14. 12
      server/lemmy_db/src/post_view.rs
  15. 6
      server/lemmy_db/src/private_message.rs
  16. 79
      server/lemmy_db/src/schema.rs
  17. 2
      server/lemmy_db/src/site.rs
  18. 2
      server/lemmy_db/src/site_view.rs
  19. 14
      server/lemmy_db/src/user.rs
  20. 174
      server/lemmy_db/src/user_ban_id.rs
  21. 10
      server/lemmy_db/src/user_mention.rs
  22. 52
      server/lemmy_db/src/user_view.rs
  23. 39
      server/lemmy_rate_limit/src/lib.rs
  24. 12
      server/lemmy_rate_limit/src/rate_limiter.rs
  25. 14
      server/lemmy_utils/src/lib.rs
  26. 6
      server/lemmy_utils/src/settings.rs
  27. 15
      server/migrations/2021-05-13-171343_comms_to_autosubscribe/down.sql
  28. 17
      server/migrations/2021-05-13-171343_comms_to_autosubscribe/up.sql
  29. 45
      server/src/api/comment.rs
  30. 39
      server/src/api/community.rs
  31. 5
      server/src/api/community_settings.rs
  32. 20
      server/src/api/mod.rs
  33. 39
      server/src/api/post.rs
  34. 8
      server/src/api/report.rs
  35. 2
      server/src/api/site.rs
  36. 132
      server/src/api/user.rs
  37. 8
      server/src/apub/activity_queue.rs
  38. 34
      server/src/apub/comment.rs
  39. 22
      server/src/apub/community.rs
  40. 11
      server/src/apub/fetcher.rs
  41. 9
      server/src/apub/inbox/activities/announce.rs
  42. 8
      server/src/apub/inbox/activities/create.rs
  43. 13
      server/src/apub/inbox/activities/delete.rs
  44. 7
      server/src/apub/inbox/activities/dislike.rs
  45. 7
      server/src/apub/inbox/activities/like.rs
  46. 13
      server/src/apub/inbox/activities/remove.rs
  47. 16
      server/src/apub/inbox/activities/undo.rs
  48. 8
      server/src/apub/inbox/activities/update.rs
  49. 10
      server/src/apub/inbox/community_inbox.rs
  50. 12
      server/src/apub/inbox/shared_inbox.rs
  51. 6
      server/src/apub/inbox/user_inbox.rs
  52. 9
      server/src/apub/mod.rs
  53. 24
      server/src/apub/post.rs
  54. 23
      server/src/apub/private_message.rs
  55. 20
      server/src/apub/user.rs
  56. 19
      server/src/main.rs
  57. 10
      server/src/routes/api.rs
  58. 3
      server/src/routes/feeds.rs
  59. 5
      server/src/routes/images.rs
  60. 5
      server/src/routes/webfinger.rs
  61. 11
      server/src/websocket/chat_server.rs
  62. 3
      server/src/websocket/handlers.rs

24
server/config/config.hjson

@ -10,4 +10,28 @@
hcaptcha_site_key: "10000000-ffff-ffff-ffff-000000000001"
hcaptcha_verify_url: "https://hcaptcha.com/siteverify"
}
rate_limit: {
# maximum number of messages created in interval
message: 180
# interval length for message limit
message_per_second: 60
# maximum number of posts created in interval
post: 6
# interval length for post limit
post_per_second: 600
# maximum number of registrations in interval
register: 2
# interval length for registration limit
register_per_second: 3600
# maximum number of image uploads in interval
image: 6
# interval length for image uploads
image_per_second: 3600
comment: 4
comment_per_second: 120
report: 4
report_per_second: 600
direct_message: 4
direct_message_per_second: 120
}
}

6
server/config/defaults.hjson

@ -55,6 +55,12 @@
image: 6
# interval length for image uploads
image_per_second: 3600
comment: 4
comment_per_second: 120
report: 4
report_per_second: 600
direct_message: 4
direct_message_per_second: 120
}
# settings related to activitypub federation
federation: {

2
server/lemmy_api_structs/src/community.rs

@ -140,4 +140,4 @@ pub struct CommunityJoinRoom {
#[derive(Serialize, Deserialize)]
pub struct CommunityJoinRoomResponse {
pub community_id: i32,
}
}

2
server/lemmy_api_structs/src/lib.rs

@ -24,4 +24,4 @@ impl APIError {
message: msg.to_string(),
}
}
}
}

19
server/lemmy_api_structs/src/site.rs

@ -1,12 +1,6 @@
use lemmy_db::{
category::*,
comment_view::*,
community_view::*,
moderator_views::*,
post_view::*,
site_view::*,
user::*,
user_view::*,
category::*, comment_view::*, community_view::*, moderator_views::*, post_view::*, site_view::*,
user::*, user_view::*,
};
use serde::{Deserialize, Serialize};
@ -45,18 +39,18 @@ pub struct GetModlog {
pub community_id: Option<i32>,
pub page: Option<i64>,
pub limit: Option<i64>,
pub action_filter: Option<u16>, //9 bits for each type of mod action
pub auth: Option<String>, // hexbear
pub action_filter: Option<u16>, //9 bits for each type of mod action
pub auth: Option<String>, // hexbear
}
//exists in upstream, but is different from hexbear ----------
#[derive(Serialize)]
pub struct GetModlogResponse {
pub log: Vec<ModlogAction>
pub log: Vec<ModlogAction>,
}
#[derive(Serialize, Deserialize)]
#[serde(tag = "type")] //enum type is inline with the internal data
#[serde(tag = "type")] //enum type is inline with the internal data
pub enum ModlogAction {
RemovePost(ModRemovePostView),
LockPost(ModLockPostView),
@ -93,6 +87,7 @@ pub struct EditSite {
pub enable_nsfw: bool,
pub auth: String,
pub enable_create_communities: Option<bool>,
pub autosubscribe_comms: Vec<i32>,
}
#[derive(Serialize, Deserialize)]

4
server/lemmy_db/src/activity.rs

@ -85,9 +85,7 @@ mod tests {
activity::{Activity, ActivityForm},
tests::establish_unpooled_connection,
user::{UserForm, User_},
Crud,
ListingType,
SortType,
Crud, ListingType, SortType,
};
use serde_json::Value;

35
server/lemmy_db/src/comment.rs

@ -2,10 +2,7 @@ use super::post::Post;
use crate::{
naive_now,
schema::{comment, comment_like, comment_report, comment_saved},
Crud,
Likeable,
Reportable,
Saveable,
Crud, Likeable, Reportable, Saveable,
};
use diesel::{dsl::*, result::Error, sql_types::Integer, *};
use serde::{Deserialize, Serialize};
@ -199,15 +196,19 @@ impl Comment {
.get_result::<Self>(conn)
}
pub fn permadelete_user_comments(conn: &PgConnection, for_creator_id: i32) -> Result<Vec<i32>, Error> {
pub fn permadelete_user_comments(
conn: &PgConnection,
for_creator_id: i32,
) -> Result<Vec<i32>, Error> {
use crate::schema::comment::dsl::*;
diesel::update(
comment
.filter(creator_id.eq(for_creator_id))
)
.set((deleted.eq(true), updated.eq(naive_now()), content.eq("*Permananently Deleted*")))
.returning(id)
.get_results(conn)
diesel::update(comment.filter(creator_id.eq(for_creator_id)))
.set((
deleted.eq(true),
updated.eq(naive_now()),
content.eq("*Permananently Deleted*"),
))
.returning(id)
.get_results(conn)
}
pub fn remove_user_comments(conn: &PgConnection, for_creator_id: i32) -> Result<Vec<i32>, Error> {
@ -370,14 +371,8 @@ impl Saveable<CommentSavedForm> for CommentSaved {
#[cfg(test)]
mod tests {
use crate::{
comment::*,
community::*,
post::*,
tests::establish_unpooled_connection,
user::*,
Crud,
ListingType,
SortType,
comment::*, community::*, post::*, tests::establish_unpooled_connection, user::*, Crud,
ListingType, SortType,
};
#[test]

17
server/lemmy_db/src/comment_view.rs

@ -225,7 +225,11 @@ impl<'a> CommentQueryBuilder<'a> {
};
//in these cases listingtype doesn't matter
if self.for_post_id.is_none() && self.for_comment_ids.is_none() && self.for_community_id.is_none() && self.for_creator_id.is_none() {
if self.for_post_id.is_none()
&& self.for_comment_ids.is_none()
&& self.for_community_id.is_none()
&& self.for_creator_id.is_none()
{
query = match self.listing_type {
ListingType::Subscribed => query.filter(subscribed.eq(true)),
ListingType::Local => query.filter(community_local.eq(true)),
@ -481,15 +485,8 @@ impl<'a> ReplyQueryBuilder<'a> {
#[cfg(test)]
mod tests {
use crate::{
comment::*,
comment_view::*,
community::*,
post::*,
tests::establish_unpooled_connection,
user::*,
Crud,
Likeable,
*,
comment::*, comment_view::*, community::*, post::*, tests::establish_unpooled_connection,
user::*, Crud, Likeable, *,
};
#[test]

7
server/lemmy_db/src/community.rs

@ -2,10 +2,7 @@ use crate::{
community_settings::CommunitySettings,
naive_now,
schema::{community, community_follower, community_moderator, community_user_ban},
Bannable,
Crud,
Followable,
Joinable,
Bannable, Crud, Followable, Joinable,
};
use diesel::{dsl::*, result::Error, *};
use serde::{Deserialize, Serialize};
@ -155,7 +152,7 @@ impl Community {
.append(&mut UserViewSafe::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())?);
mods_and_admins
.append(&mut UserViewSafe::sitemods(conn).map(|v| v.into_iter().map(|a| a.id).collect())?);
.append(&mut UserViewSafe::sitemods(conn).map(|v| v.into_iter().map(|a| a.id).collect())?);
Ok(mods_and_admins)
}

2
server/lemmy_db/src/lib.rs

@ -45,8 +45,8 @@ pub mod user_tag;
pub mod user_view;
// hexbear
pub mod user_token;
pub mod user_ban_id;
pub mod user_token;
pub trait Crud<T> {
fn create(conn: &PgConnection, form: &T) -> Result<Self, Error>

21
server/lemmy_db/src/moderator.rs

@ -1,14 +1,7 @@
use crate::{
schema::{
mod_add,
mod_add_community,
mod_ban,
mod_ban_from_community,
mod_lock_post,
mod_remove_comment,
mod_remove_community,
mod_remove_post,
mod_sticky_post,
mod_add, mod_add_community, mod_ban, mod_ban_from_community, mod_lock_post, mod_remove_comment,
mod_remove_community, mod_remove_post, mod_sticky_post,
},
Crud,
};
@ -414,14 +407,8 @@ impl Crud<ModAddForm> for ModAdd {
#[cfg(test)]
mod tests {
use crate::{
comment::*,
community::*,
moderator::*,
post::*,
tests::establish_unpooled_connection,
user::*,
ListingType,
SortType,
comment::*, community::*, moderator::*, post::*, tests::establish_unpooled_connection, user::*,
ListingType, SortType,
};
// use Crud;

7
server/lemmy_db/src/password_reset_request.rs

@ -80,11 +80,8 @@ impl PasswordResetRequest {
mod tests {
use super::super::user::*;
use crate::{
password_reset_request::PasswordResetRequest,
tests::establish_unpooled_connection,
Crud,
ListingType,
SortType,
password_reset_request::PasswordResetRequest, tests::establish_unpooled_connection, Crud,
ListingType, SortType,
};
#[test]

36
server/lemmy_db/src/post.rs

@ -1,11 +1,7 @@
use crate::{
naive_now,
schema::{post, post_like, post_read, post_report, post_saved},
Crud,
Likeable,
Readable,
Reportable,
Saveable,
Crud, Likeable, Readable, Reportable, Saveable,
};
use diesel::{dsl::*, result::Error, *};
use serde::{Deserialize, Serialize};
@ -209,24 +205,25 @@ impl Post {
.get_result::<Self>(conn)
}
pub fn permadelete_user_posts(conn: &PgConnection, for_creator_id: i32) -> Result<Vec<i32>, Error> {
pub fn permadelete_user_posts(
conn: &PgConnection,
for_creator_id: i32,
) -> Result<Vec<i32>, Error> {
use crate::schema::post::dsl::*;
let perma_deleted = "*Permananently Deleted*";
let perma_deleted_url = "https://deleted.com";
diesel::update(
post
.filter(creator_id.eq(for_creator_id)),
)
.set((
diesel::update(post.filter(creator_id.eq(for_creator_id)))
.set((
deleted.eq(true),
updated.eq(naive_now()),
name.eq(perma_deleted),
url.eq(perma_deleted_url),
body.eq(perma_deleted)))
.returning(id)
.get_results(conn)
name.eq(perma_deleted),
url.eq(perma_deleted_url),
body.eq(perma_deleted),
))
.returning(id)
.get_results(conn)
}
pub fn update_featured(
@ -447,12 +444,7 @@ impl Readable<PostReadForm> for PostRead {
#[cfg(test)]
mod tests {
use crate::{
community::*,
post::*,
tests::establish_unpooled_connection,
user::*,
ListingType,
SortType,
community::*, post::*, tests::establish_unpooled_connection, user::*, ListingType, SortType,
};
#[test]

12
server/lemmy_db/src/post_view.rs

@ -252,7 +252,7 @@ impl<'a> PostQueryBuilder<'a> {
query = query.filter(community_hide_from_all.eq(false).or(subscribed.eq(true)));
}
query
},
}
_ => query,
};
@ -380,14 +380,8 @@ impl PostView {
#[cfg(test)]
mod tests {
use crate::{
community::*,
post::*,
post_view::*,
tests::establish_unpooled_connection,
user::*,
Crud,
Likeable,
*,
community::*, post::*, post_view::*, tests::establish_unpooled_connection, user::*, Crud,
Likeable, *,
};
#[test]

6
server/lemmy_db/src/private_message.rs

@ -138,11 +138,7 @@ impl PrivateMessage {
#[cfg(test)]
mod tests {
use crate::{
private_message::*,
tests::establish_unpooled_connection,
user::*,
ListingType,
SortType,
private_message::*, tests::establish_unpooled_connection, user::*, ListingType, SortType,
};
#[test]

79
server/lemmy_db/src/schema.rs

@ -9,7 +9,7 @@ table! {
}
}
table!{
table! {
hexbear.ban_id (id) {
id -> Uuid,
created -> Timestamp,
@ -468,6 +468,7 @@ table! {
enable_create_communities -> Bool,
icon -> Nullable<Text>,
banner -> Nullable<Text>,
autosubscribe_comms -> Array<Int4>,
}
}
@ -627,43 +628,43 @@ joinable!(user_tag -> user_ (user_id));
joinable!(user_tokens -> user_ (user_id));
allow_tables_to_appear_in_same_query!(
activity,
category,
comment,
comment_aggregates_fast,
comment_like,
comment_report,
comment_saved,
community,
community_aggregates_fast,
community_follower,
community_moderator,
community_settings,
community_user_ban,
community_user_tag,
mod_add,
mod_add_community,
mod_ban,
mod_ban_from_community,
mod_lock_post,
mod_remove_comment,
mod_remove_community,
mod_remove_post,
mod_sticky_post,
password_reset_request,
post,
post_aggregates_fast,
post_like,
post_read,
post_report,
post_saved,
private_message,
site,
user_,
user_ban,
user_ban_id,
user_fast,
user_mention,
user_tag,
activity,
category,
comment,
comment_aggregates_fast,
comment_like,
comment_report,
comment_saved,
community,
community_aggregates_fast,
community_follower,
community_moderator,
community_settings,
community_user_ban,
community_user_tag,
mod_add,
mod_add_community,
mod_ban,
mod_ban_from_community,
mod_lock_post,
mod_remove_comment,
mod_remove_community,
mod_remove_post,
mod_sticky_post,
password_reset_request,
post,
post_aggregates_fast,
post_like,
post_read,
post_report,
post_saved,
private_message,
site,
user_,
user_ban,
user_ban_id,
user_fast,
user_mention,
user_tag,
user_tokens,
);

2
server/lemmy_db/src/site.rs

@ -17,6 +17,7 @@ pub struct Site {
pub enable_create_communities: bool,
pub icon: Option<String>,
pub banner: Option<String>,
pub autosubscribe_comms: Vec<i32>,
}
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize)]
@ -33,6 +34,7 @@ pub struct SiteForm {
// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
pub icon: Option<Option<String>>,
pub banner: Option<Option<String>>,
pub autosubscribe_comms: Vec<i32>,
}
impl Crud<SiteForm> for Site {

2
server/lemmy_db/src/site_view.rs

@ -15,6 +15,7 @@ table! {
enable_create_communities -> Bool,
icon -> Nullable<Text>,
banner -> Nullable<Text>,
autosubscribe_comms -> Array<Int4>,
creator_name -> Varchar,
creator_preferred_username -> Nullable<Varchar>,
creator_avatar -> Nullable<Text>,
@ -42,6 +43,7 @@ pub struct SiteView {
pub enable_create_communities: bool,
pub icon: Option<String>,
pub banner: Option<String>,
pub autosubscribe_comms: Vec<i32>,
pub creator_name: String,
pub creator_preferred_username: Option<String>,
pub creator_avatar: Option<String>,

14
server/lemmy_db/src/user.rs

@ -1,6 +1,5 @@
use crate::{
is_email_regex,
naive_now,
is_email_regex, naive_now,
schema::{user_, user_::dsl::*},
Crud,
};
@ -124,10 +123,15 @@ impl User_ {
.get_result::<Self>(conn)
}
pub fn update_username(conn: &PgConnection, user_id: i32, new_uname: String, new_actor: String) -> Result<Self, Error> {
pub fn update_username(
conn: &PgConnection,
user_id: i32,
new_uname: String,
new_actor: String,
) -> Result<Self, Error> {
diesel::update(user_.find(user_id))
.set((name.eq(new_uname.clone()), actor_id.eq(new_actor)))
.get_result::<Self>(conn)
.set((name.eq(new_uname.clone()), actor_id.eq(new_actor)))
.get_result::<Self>(conn)
}
pub fn read_from_name(conn: &PgConnection, from_user_name: &str) -> Result<Self, Error> {

174
server/lemmy_db/src/user_ban_id.rs

@ -1,102 +1,136 @@
use diesel::{dsl::*, result::Error, *};
use crate::schema::{
{ban_id, ban_id::dsl::*},
{user_ban_id, user_ban_id::dsl::*},
{ban_id, ban_id::dsl::*},
{user_ban_id, user_ban_id::dsl::*},
};
use uuid::Uuid;
use crate::user_view::UserViewSafe;
use diesel::{dsl::*, result::Error, *};
use uuid::Uuid;
#[derive(Queryable, Insertable)]
#[table_name = "ban_id"]
pub struct BanId {
pub id: Uuid,
pub created: chrono::NaiveDateTime,
pub aliased_to: Option<Uuid>,
pub id: Uuid,
pub created: chrono::NaiveDateTime,
pub aliased_to: Option<Uuid>,
}
#[derive(Queryable, Insertable)]
#[table_name = "user_ban_id"]
pub struct UserBanId {
pub bid: Uuid,
pub uid: i32,
pub bid: Uuid,
pub uid: i32,
}
#[derive(Queryable)]
pub struct UserRelationResp {
pub bid: Uuid,
pub uid: i32,
pub name: String,
pub banned: bool,
pub bid: Uuid,
pub uid: i32,
pub name: String,
pub banned: bool,
}
impl BanId {
pub fn create(conn: &PgConnection) -> Result<Self, Error> {
insert_into(ban_id).default_values().get_result::<Self>(conn)
}
pub fn create(conn: &PgConnection) -> Result<Self, Error> {
insert_into(ban_id)
.default_values()
.get_result::<Self>(conn)
}
pub fn read(conn: &PgConnection, ban_id_val: Uuid) -> Result<Self, Error> {
ban_id.find(ban_id_val).first::<Self>(conn)
}
pub fn read(conn: &PgConnection, ban_id_val: Uuid) -> Result<Self, Error> {
ban_id.find(ban_id_val).first::<Self>(conn)
}
pub fn read_opt(conn: &PgConnection, ban_id_val: Uuid) -> Result<Option<Self>, Error> {
ban_id.find(ban_id_val).first::<Self>(conn).optional()
}
pub fn read_opt(conn: &PgConnection, ban_id_val: Uuid) -> Result<Option<Self>, Error> {
ban_id.find(ban_id_val).first::<Self>(conn).optional()
}
pub fn update_alias(conn: &PgConnection, old_bid_val: Uuid, new_bid_val: Uuid) -> Result<Vec<Self>, Error> {
update(ban_id.filter(ban_id::id.eq(old_bid_val).or(aliased_to.eq(old_bid_val)))).set(aliased_to.eq(new_bid_val)).get_results(conn)
}
pub fn update_alias(
conn: &PgConnection,
old_bid_val: Uuid,
new_bid_val: Uuid,
) -> Result<Vec<Self>, Error> {
update(ban_id.filter(ban_id::id.eq(old_bid_val).or(aliased_to.eq(old_bid_val))))
.set(aliased_to.eq(new_bid_val))
.get_results(conn)
}
}
impl UserBanId {
fn simple_associate(conn: &PgConnection, ban_id_val: Uuid, user_id_val: i32) -> Result<Self, Error> {
insert_into(user_ban_id)
.values(UserBanId { bid: ban_id_val, uid: user_id_val })
.get_result::<Self>(conn)
}
fn simple_associate(
conn: &PgConnection,
ban_id_val: Uuid,
user_id_val: i32,
) -> Result<Self, Error> {
insert_into(user_ban_id)
.values(UserBanId {
bid: ban_id_val,
uid: user_id_val,
})
.get_result::<Self>(conn)
}
fn overwriting_associate(conn: &PgConnection, old_bid_val: Uuid, new_bid_val: Uuid) -> Result<Self, Error> {
BanId::update_alias(conn, old_bid_val, new_bid_val)?;
update(user_ban_id.filter(bid.eq(old_bid_val))).set(bid.eq(new_bid_val)).get_result(conn)
}
fn overwriting_associate(
conn: &PgConnection,
old_bid_val: Uuid,
new_bid_val: Uuid,
) -> Result<Self, Error> {
BanId::update_alias(conn, old_bid_val, new_bid_val)?;
update(user_ban_id.filter(bid.eq(old_bid_val)))
.set(bid.eq(new_bid_val))
.get_result(conn)
}
pub fn associate(conn: &PgConnection, ban_id_val: Uuid, user_id_val: i32) -> Result<Self, Error> {
match Self::get_by_user(conn, &user_id_val) {
//UserBanId found attached to user, which is not the same as the incoming one.
Ok(Some(old_bid)) if old_bid.bid != ban_id_val => {
let incoming_bid = BanId::read(conn, ban_id_val)?;
//the incoming bid isn't aliased to the new one.
if incoming_bid.aliased_to.is_none() || incoming_bid.aliased_to.unwrap() != old_bid.bid {
return Self::overwriting_associate(conn, old_bid.bid, ban_id_val);
}
Ok(old_bid)
},
//UserBanId found, but it's the same as the incoming one.
Ok(Some(k)) => Ok(k),
//There wasn't any UBID attached to the user. Associate and move on.
Ok(None) => {
//Check for an alias
let bid_read = BanId::read_opt(conn, ban_id_val)?;
if let Some(BanId { aliased_to: Some(alias), .. }) = bid_read {
Self::simple_associate(conn, alias, user_id_val)
} else {
Self::simple_associate(conn, ban_id_val, user_id_val)
}
},
//Breaking error, bubble it up.
Err(e) => Err(e),
pub fn associate(conn: &PgConnection, ban_id_val: Uuid, user_id_val: i32) -> Result<Self, Error> {
match Self::get_by_user(conn, &user_id_val) {
//UserBanId found attached to user, which is not the same as the incoming one.
Ok(Some(old_bid)) if old_bid.bid != ban_id_val => {
let incoming_bid = BanId::read(conn, ban_id_val)?;
//the incoming bid isn't aliased to the new one.
if incoming_bid.aliased_to.is_none() || incoming_bid.aliased_to.unwrap() != old_bid.bid {
return Self::overwriting_associate(conn, old_bid.bid, ban_id_val);
}
Ok(old_bid)
}
//UserBanId found, but it's the same as the incoming one.
Ok(Some(k)) => Ok(k),
//There wasn't any UBID attached to the user. Associate and move on.
Ok(None) => {
//Check for an alias
let bid_read = BanId::read_opt(conn, ban_id_val)?;
if let Some(BanId {
aliased_to: Some(alias),
..
}) = bid_read
{
Self::simple_associate(conn, alias, user_id_val)
} else {
Self::simple_associate(conn, ban_id_val, user_id_val)
}
}
//Breaking error, bubble it up.
Err(e) => Err(e),
}
}
pub fn create_then_associate(conn: &PgConnection, user_id_val: i32) -> Result<Self, Error> {
Self::simple_associate(conn, BanId::create(conn)?.id, user_id_val)
}
pub fn create_then_associate(conn: &PgConnection, user_id_val: i32) -> Result<Self, Error> {
Self::simple_associate(conn, BanId::create(conn)?.id, user_id_val)
}
pub fn get_by_user(conn: &PgConnection, user_id_val: &i32) -> Result<Option<Self>, Error> {
user_ban_id.filter(uid.eq(user_id_val)).first::<Self>(conn).optional()
}
pub fn get_by_user(conn: &PgConnection, user_id_val: &i32) -> Result<Option<Self>, Error> {
user_ban_id
.filter(uid.eq(user_id_val))
.first::<Self>(conn)
.optional()
}
pub fn get_users_by_bid(conn: &PgConnection, ban_id_val: Uuid) -> Result<Vec<UserViewSafe>, Error> {
let uids = user_ban_id.filter(bid.eq(ban_id_val)).select(uid).load(conn)?;
UserViewSafe::read_mult(conn, uids)
}
}
pub fn get_users_by_bid(
conn: &PgConnection,
ban_id_val: Uuid,
) -> Result<Vec<UserViewSafe>, Error> {
let uids = user_ban_id
.filter(bid.eq(ban_id_val))
.select(uid)
.load(conn)?;
UserViewSafe::read_mult(conn, uids)
}
}

10
server/lemmy_db/src/user_mention.rs

@ -75,14 +75,8 @@ impl UserMention {
#[cfg(test)]
mod tests {
use crate::{
comment::*,
community::*,
post::*,
tests::establish_unpooled_connection,
user::*,
user_mention::*,
ListingType,
SortType,
comment::*, community::*, post::*, tests::establish_unpooled_connection, user::*,
user_mention::*, ListingType, SortType,
};
#[test]

52
server/lemmy_db/src/user_view.rs

@ -80,8 +80,8 @@ pub struct UserViewSafe {
pub moderator: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub number_of_posts: i64,
pub number_of_comments: i64,
pub number_of_posts: i64,
pub number_of_comments: i64,
}
pub struct UserQueryBuilder<'a> {
@ -103,8 +103,8 @@ pub struct UserQueryBuilder<'a> {
sql_types::Bool,
sql_types::Bool,
sql_types::Timestamp,
sql_types::BigInt,
sql_types::BigInt,
sql_types::BigInt,
sql_types::BigInt,
),
user_view::table,
Pg,
@ -134,8 +134,8 @@ impl<'a> UserQueryBuilder<'a> {
moderator,
banned,
published,
number_of_posts,
number_of_comments,
number_of_posts,
number_of_comments,
))
.into_boxed();
@ -417,26 +417,26 @@ impl UserViewSafe {
pub fn read_mult(conn: &PgConnection, from_user_ids: Vec<i32>) -> Result<Vec<Self>, Error> {
use super::user_view::user_view::dsl::*;
user_view
.select((
id,
actor_id,
name,
preferred_username,
avatar,
banner,
matrix_user_id,
bio,
local,
admin,
sitemod,
moderator,
banned,
published,
number_of_posts,
number_of_comments,
))
.filter(id.eq(any(from_user_ids)))
.load(conn)
.select((
id,
actor_id,
name,
preferred_username,
avatar,
banner,
matrix_user_id,
bio,
local,
admin,
sitemod,
moderator,
banned,
published,
number_of_posts,
number_of_comments,
))
.filter(id.eq(any(from_user_ids)))
.load(conn)
}
pub fn admins(conn: &PgConnection) -> Result<Vec<Self>, Error> {

39
server/lemmy_rate_limit/src/lib.rs

@ -58,6 +58,18 @@ impl RateLimit {
self.kind(RateLimitType::Image)
}
pub fn comment(&self) -> RateLimited {
self.kind(RateLimitType::Comment)
}
pub fn report(&self) -> RateLimited {
self.kind(RateLimitType::Report)
}
pub fn direct_message(&self) -> RateLimited {
self.kind(RateLimitType::DirectMessage)
}
fn kind(&self, type_: RateLimitType) -> RateLimited {
RateLimited {
rate_limiter: self.rate_limiter.clone(),
@ -123,6 +135,33 @@ impl RateLimited {
false,
)?;
}
RateLimitType::Comment => {
limiter.check_rate_limit_full(
self.type_,
&ip_addr,
rate_limit.comment,
rate_limit.comment_per_second,
false,
)?;
}
RateLimitType::Report => {
limiter.check_rate_limit_full(
self.type_,
&ip_addr,
rate_limit.report,
rate_limit.report_per_second,
false,
)?;
}
RateLimitType::DirectMessage => {
limiter.check_rate_limit_full(
self.type_,
&ip_addr,
rate_limit.direct_message,
rate_limit.direct_message_per_second,
false,
)?;
}
};
}

12
server/lemmy_rate_limit/src/rate_limiter.rs

@ -16,6 +16,9 @@ pub enum RateLimitType {
Register,
Post,
Image,
Comment,
Report,
DirectMessage,
}
/// Rate limiting based on rate type and IP addr
@ -79,7 +82,7 @@ impl RateLimiter {
rate_limit.allowance = rate as f64;
}
if rate_limit.allowance < 1.0 && ip != "127.0.0.1" {
if rate_limit.allowance < 1.0 {
debug!(
"Rate limited type: {}, IP: {}, time_passed: {}, allowance: {}",
type_.as_ref(),
@ -90,17 +93,16 @@ impl RateLimiter {
Err(
APIError {
message: format!(
"Too many requests. type: {}, IP: {}, {} per {} seconds",
type_.as_ref(),
ip,
"Too many requests. You are limited to {} {}s per {} seconds",
rate,
type_.as_ref(),
per
),
}
.into(),
)
} else {
if !check_only && ip != "127.0.0.1" {
if !check_only {
rate_limit.allowance -= 1.0;
}
Ok(())

14
server/lemmy_utils/src/lib.rs

@ -23,9 +23,7 @@ use lettre::{
extension::ClientId,
ConnectionReuseParameters,
},
ClientSecurity,
SmtpClient,
Transport,
ClientSecurity, SmtpClient, Transport,
};
use lettre_email::Email;
use openssl::{pkey::PKey, rsa::Rsa};
@ -247,14 +245,8 @@ pub fn is_valid_post_title(title: &str) -> bool {
#[cfg(test)]
mod tests {
use crate::{
is_valid_community_name,
is_valid_post_title,
is_valid_preferred_username,
is_valid_username,
remove_slurs,
scrape_text_for_mentions,
slur_check,
slurs_vec_to_str,
is_valid_community_name, is_valid_post_title, is_valid_preferred_username, is_valid_username,
remove_slurs, scrape_text_for_mentions, slur_check, slurs_vec_to_str,
};
#[test]

6
server/lemmy_utils/src/settings.rs

@ -41,6 +41,12 @@ pub struct RateLimitConfig {
pub register_per_second: i32,
pub image: i32,
pub image_per_second: i32,
pub comment: i32,
pub comment_per_second: i32,
pub report: i32,
pub report_per_second: i32,
pub direct_message: i32,
pub direct_message_per_second: i32,
}
#[derive(Debug, Deserialize, Clone)]

15
server/migrations/2021-05-13-171343_comms_to_autosubscribe/down.sql

@ -0,0 +1,15 @@
-- This file should undo anything in `up.sql`
drop view site_view;
alter table site drop column autosubscribe_comms;
create view site_view as
select s.*,
u.name as creator_name,
u.preferred_username as creator_preferred_username,
u.avatar as creator_avatar,
(select count(*) from user_) as number_of_users,
(select count(*) from post) as number_of_posts,
(select count(*) from comment) as number_of_comments,
(select count(*) from community) as number_of_communities
from site s
left join user_ u on s.creator_id = u.id;

17
server/migrations/2021-05-13-171343_comms_to_autosubscribe/up.sql

@ -0,0 +1,17 @@
-- Your SQL goes here
alter table site add column autosubscribe_comms integer[] default '{}' not null;
drop view site_view;
create view site_view as
select s.*,
u.name as creator_name,
u.preferred_username as creator_preferred_username,
u.avatar as creator_avatar,
(select count(*) from user_) as number_of_users,
(select count(*) from post) as number_of_posts,
(select count(*) from comment) as number_of_comments,
(select count(*) from community) as number_of_communities
from site s
left join user_ u on s.creator_id = u.id;

45
server/src/api/comment.rs

@ -4,57 +4,30 @@ use actix_web::web::Data;
use log::error;
use lemmy_api_structs::{comment::*, APIError};
use lemmy_db::{
comment::*,
comment_view::*,
community_settings::*,
moderator::*,
post::*,
site_view::*,
user::*,
user_mention::*,
Crud,
Likeable,
ListingType,
Saveable,
SortType,
};
use lemmy_db::{comment::*, comment_view::*, community_settings::*, moderator::*, post::*, site_view::*, user::*, user_mention::*, Crud, Likeable, ListingType, Saveable, SortType, naive_now};
use lemmy_utils::{
make_apub_endpoint,
num_md_images,
remove_slurs,
scrape_text_for_mentions,
send_email,
settings::Settings,
ConnectionId,
EndpointType,
LemmyError,
MentionData,
make_apub_endpoint, num_md_images, remove_slurs, scrape_text_for_mentions, send_email,
settings::Settings, ConnectionId, EndpointType, LemmyError, MentionData,
};
use crate::{
api::{
check_community_ban,
get_post,
get_user_from_jwt,
get_user_from_jwt_opt,
is_mod_or_admin,
check_community_ban, get_post, get_user_from_jwt, get_user_from_jwt_opt, is_mod_or_admin,
Perform,
},
apub::{ApubLikeableType, ApubObjectType},
blocking,
is_within_comment_char_limit,
blocking, is_within_comment_char_limit,
websocket::{
messages::{JoinCommunityRoom, SendComment},
UserOperation,
},
DbPool,
LemmyContext,
DbPool, LemmyContext,
};
use lemmy_db::{
community_view::{CommunityModeratorView, CommunityView},
post_view::PostView,
};
use crate::chrono::Duration;
#[async_trait::async_trait(?Send)]
impl Perform for GetComment {
@ -161,6 +134,10 @@ impl Perform for CreateComment {
let data: &CreateComment = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?;
if (naive_now() - user.published) < Duration::minutes(5) {
return Err(APIError::err("new_user_5min_waiting_period_not_met").into());
}
let content_slurs_removed = remove_slurs(&data.content.to_owned());
// let content_pii_removed = remove_pii(&content_slurs_removed);

39
server/src/api/community.rs

@ -5,44 +5,19 @@ use anyhow::Context;
use lemmy_api_structs::{community::*, APIError};
use lemmy_db::{
comment::Comment,
comment_view::CommentQueryBuilder,
community::*,
community_settings::*,
community_view::*,
diesel_option_overwrite,
moderator::*,
naive_now,
post::Post,
site::*,
user_view::*,
Bannable,
Crud,
Followable,
Joinable,
SortType,
comment::Comment, comment_view::CommentQueryBuilder, community::*, community_settings::*,
community_view::*, diesel_option_overwrite, moderator::*, naive_now, post::Post, site::*,
user_view::*, Bannable, Crud, Followable, Joinable, SortType,
};
use lemmy_utils::{
generate_actor_keypair,
is_valid_community_name,
location_info,
make_apub_endpoint,
naive_from_unix,
ConnectionId,
EndpointType,
LemmyError,
generate_actor_keypair, is_valid_community_name, location_info, make_apub_endpoint,
naive_from_unix, ConnectionId, EndpointType, LemmyError,
};
use crate::{
api::{
check_slurs,
check_slurs_opt,
get_user_from_jwt,
get_user_from_jwt_opt,
is_admin,
is_admin_or_sitemod,
is_mod_or_admin,
Perform,
check_slurs, check_slurs_opt, get_user_from_jwt, get_user_from_jwt_opt, is_admin,
is_admin_or_sitemod, is_mod_or_admin, Perform,
},
apub::ActorType,
blocking,

5
server/src/api/community_settings.rs

@ -3,8 +3,7 @@ use actix_web::web::Data;
use lemmy_api_structs::{community_settings::*, APIError};
use lemmy_db::{
community_settings::{CommunitySettings, CommunitySettingsForm},
naive_now,
Crud,
naive_now, Crud,
};
use lemmy_utils::{ConnectionId, LemmyError};
@ -91,7 +90,7 @@ impl Perform for EditCommunitySettings {
comment_images: updated_community_settings.comment_images,
published: updated_community_settings.published,
allow_as_default: updated_community_settings.allow_as_default,
hide_from_all: updated_community_settings.hide_from_all,
hide_from_all: updated_community_settings.hide_from_all,
};
context.chat_server().do_send(SendCommunityRoomMessage {

20
server/src/api/mod.rs

@ -1,13 +1,16 @@
use actix_web::web::Data;
use lemmy_api_structs::APIError;
use lemmy_db::{Crud, community::Community, community_view::CommunityUserBanView, naive_now, post::Post, user::User_};
use lemmy_db::{
community::Community, community_view::CommunityUserBanView, naive_now, post::Post, user::User_,
Crud,
};
use lemmy_utils::{settings::Settings, slur_check, slurs_vec_to_str, ConnectionId, LemmyError};
use crate::{api::claims::Claims, blocking, DbPool, LemmyContext};
use chrono::Duration;
use lemmy_db::user_token::UserToken;
use lemmy_db::user_ban_id::UserBanId;
use lemmy_db::user_token::UserToken;
pub mod claims;
pub mod comment;
@ -86,7 +89,9 @@ pub(in crate::api) async fn get_user_from_jwt(
if !bid_string.is_empty() {
//bid reported, try creating relationship
let bid = bid_string.parse().map_err(|_| APIError::err("invalid_bid"))?;
let bid = bid_string
.parse()
.map_err(|_| APIError::err("invalid_bid"))?;
blocking(pool, move |conn| UserBanId::associate(conn, bid, user_id)).await??;
} else {
//bid not reported, find existing
@ -102,9 +107,14 @@ pub(in crate::api) async fn get_user_from_jwt(
if user.banned {
//generate new bid
if bid_string.is_empty() {
bid_string = blocking(pool, move |conn| UserBanId::create_then_associate(conn, user_id.clone())).await??.bid.to_string();
bid_string = blocking(pool, move |conn| {
UserBanId::create_then_associate(conn, user_id.clone())
})
.await??
.bid
.to_string();
}
return Err(APIError::err(&*format!("site_ban_{}", bid_string)).into());
}
Ok(user)

39
server/src/api/post.rs

@ -5,44 +5,21 @@ use url::Url;
use lemmy_api_structs::{post::*, APIError};
use lemmy_db::{
comment_view::*,
community_settings::*,
community_view::*,
moderator::*,
naive_now,
post::*,
post_view::*,
site::*,
site_view::*,
user_view::*,
Crud,
Likeable,
ListingType,
Saveable,
comment_view::*, community_settings::*, community_view::*, moderator::*, naive_now, post::*,
post_view::*, site::*, site_view::*, user_view::*, Crud, Likeable, ListingType, Saveable,
SortType,
};
use lemmy_utils::{
is_valid_post_title,
make_apub_endpoint,
ConnectionId,
EndpointType,
LemmyError,
is_valid_post_title, make_apub_endpoint, ConnectionId, EndpointType, LemmyError,
};
use crate::{
api::{
check_community_ban,
check_slurs,
check_slurs_opt,