From 8708ad1b44e58dfbd8c555f9bc270daadbfa01af Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 22 Sep 2021 11:57:09 -0400 Subject: [PATCH] Moving settings and secrets to context. --- crates/api/src/comment.rs | 9 +- crates/api/src/comment_report.rs | 9 +- crates/api/src/community.rs | 15 ++- crates/api/src/lib.rs | 16 ++- crates/api/src/local_user.rs | 82 ++++++++----- crates/api/src/post.rs | 12 +- crates/api/src/post_report.rs | 9 +- crates/api/src/private_message.rs | 3 +- crates/api/src/site.rs | 24 ++-- crates/api/src/websocket.rs | 3 +- crates/api_common/src/lib.rs | 61 +++++++--- crates/api_crud/src/comment/create.rs | 16 ++- crates/api_crud/src/comment/delete.rs | 8 +- crates/api_crud/src/comment/read.rs | 5 +- crates/api_crud/src/comment/update.rs | 7 +- crates/api_crud/src/community/create.rs | 17 ++- crates/api_crud/src/community/delete.rs | 6 +- crates/api_crud/src/community/read.rs | 9 +- crates/api_crud/src/community/update.rs | 7 +- crates/api_crud/src/post/create.rs | 18 ++- crates/api_crud/src/post/delete.rs | 6 +- crates/api_crud/src/post/read.rs | 8 +- crates/api_crud/src/post/update.rs | 11 +- crates/api_crud/src/private_message/create.rs | 9 +- crates/api_crud/src/private_message/delete.rs | 3 +- crates/api_crud/src/private_message/read.rs | 3 +- crates/api_crud/src/private_message/update.rs | 5 +- crates/api_crud/src/site/create.rs | 7 +- crates/api_crud/src/site/read.rs | 14 ++- crates/api_crud/src/site/update.rs | 7 +- crates/api_crud/src/user/create.rs | 30 +++-- crates/api_crud/src/user/delete.rs | 3 +- crates/api_crud/src/user/read.rs | 6 +- .../activities/comment/create_or_update.rs | 7 +- crates/apub/src/activities/comment/mod.rs | 26 +++-- .../apub/src/activities/community/add_mod.rs | 7 +- .../apub/src/activities/community/announce.rs | 7 +- .../src/activities/community/block_user.rs | 10 +- crates/apub/src/activities/community/mod.rs | 10 +- .../src/activities/community/remove_mod.rs | 7 +- .../activities/community/undo_block_user.rs | 9 +- .../apub/src/activities/community/update.rs | 15 ++- crates/apub/src/activities/deletion/delete.rs | 10 +- .../src/activities/deletion/undo_delete.rs | 9 +- .../apub/src/activities/following/accept.rs | 7 +- .../apub/src/activities/following/follow.rs | 10 +- crates/apub/src/activities/following/undo.rs | 9 +- crates/apub/src/activities/mod.rs | 8 +- .../src/activities/post/create_or_update.rs | 7 +- .../private_message/create_or_update.rs | 7 +- .../src/activities/private_message/delete.rs | 10 +- .../activities/private_message/undo_delete.rs | 9 +- crates/apub/src/activities/send/community.rs | 10 +- crates/apub/src/activities/undo_remove.rs | 2 +- .../apub/src/activities/voting/undo_vote.rs | 9 +- crates/apub/src/activities/voting/vote.rs | 7 +- crates/apub/src/activity_queue.rs | 6 +- crates/apub/src/fetcher/community.rs | 19 ++- crates/apub/src/fetcher/fetch.rs | 5 +- crates/apub/src/fetcher/objects.rs | 107 +++++++++++++++++ crates/apub/src/fetcher/person.rs | 80 +++++++++++++ crates/apub/src/fetcher/search.rs | 9 +- crates/apub/src/http/mod.rs | 15 ++- crates/apub/src/http/routes.rs | 6 +- crates/apub/src/lib.rs | 28 ++--- crates/apub/src/objects/comment.rs | 2 +- crates/apub/src/objects/community.rs | 11 +- crates/apub/src/objects/person.rs | 10 +- crates/apub/src/objects/post.rs | 9 +- crates/apub_lib/src/webfinger.rs | 8 +- crates/db_queries/src/source/secret.rs | 32 ++--- crates/routes/src/feeds.rs | 109 +++++++++--------- crates/routes/src/images.rs | 43 ++++--- crates/routes/src/nodeinfo.rs | 10 +- crates/routes/src/webfinger.rs | 19 ++- crates/utils/src/claims.rs | 6 +- crates/utils/src/email.rs | 5 +- crates/utils/src/lib.rs | 16 +-- crates/utils/src/rate_limit/mod.rs | 12 +- crates/utils/src/request.rs | 12 +- crates/utils/src/settings/mod.rs | 42 ++++++- crates/utils/src/settings/structs.rs | 7 ++ crates/utils/src/test.rs | 57 +++++---- crates/utils/src/utils.rs | 42 +++---- crates/websocket/src/chat_server.rs | 16 ++- crates/websocket/src/lib.rs | 18 ++- src/code_migrations.rs | 84 ++++++++++---- src/main.rs | 24 +++- 88 files changed, 1043 insertions(+), 486 deletions(-) create mode 100644 crates/apub/src/fetcher/objects.rs create mode 100644 crates/apub/src/fetcher/person.rs diff --git a/crates/api/src/comment.rs b/crates/api/src/comment.rs index 0af3324fa..1d1d3088f 100644 --- a/crates/api/src/comment.rs +++ b/crates/api/src/comment.rs @@ -32,7 +32,8 @@ impl Perform for MarkCommentAsRead { _websocket_id: Option, ) -> Result { let data: &MarkCommentAsRead = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let comment_id = data.comment_id; let orig_comment = blocking(context.pool(), move |conn| { @@ -88,7 +89,8 @@ impl Perform for SaveComment { _websocket_id: Option, ) -> Result { let data: &SaveComment = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let comment_saved_form = CommentSavedForm { comment_id: data.comment_id, @@ -132,7 +134,8 @@ impl Perform for CreateCommentLike { websocket_id: Option, ) -> Result { let data: &CreateCommentLike = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let mut recipient_ids = Vec::::new(); diff --git a/crates/api/src/comment_report.rs b/crates/api/src/comment_report.rs index 83aebfc52..6d2e2280d 100644 --- a/crates/api/src/comment_report.rs +++ b/crates/api/src/comment_report.rs @@ -32,7 +32,8 @@ impl Perform for CreateCommentReport { websocket_id: Option, ) -> Result { let data: &CreateCommentReport = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // check size of report and check for whitespace let reason = data.reason.trim(); @@ -96,7 +97,8 @@ impl Perform for ResolveCommentReport { websocket_id: Option, ) -> Result { let data: &ResolveCommentReport = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let report_id = data.report_id; let report = blocking(context.pool(), move |conn| { @@ -149,7 +151,8 @@ impl Perform for ListCommentReports { websocket_id: Option, ) -> Result { let data: &ListCommentReports = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.person.id; let community_id = data.community; diff --git a/crates/api/src/community.rs b/crates/api/src/community.rs index d68d27c2f..28a79c701 100644 --- a/crates/api/src/community.rs +++ b/crates/api/src/community.rs @@ -53,7 +53,8 @@ impl Perform for FollowCommunity { _websocket_id: Option, ) -> Result { let data: &FollowCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let community_id = data.community_id; let community = blocking(context.pool(), move |conn| { @@ -121,7 +122,8 @@ impl Perform for BlockCommunity { _websocket_id: Option, ) -> Result { let data: &BlockCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let community_id = data.community_id; let person_id = local_user_view.person.id; @@ -181,7 +183,8 @@ impl Perform for BanFromCommunity { websocket_id: Option, ) -> Result { let data: &BanFromCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let community_id = data.community_id; let banned_person_id = data.person_id; @@ -314,7 +317,8 @@ impl Perform for AddModToCommunity { websocket_id: Option, ) -> Result { let data: &AddModToCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let community_id = data.community_id; @@ -397,7 +401,8 @@ impl Perform for TransferCommunity { _websocket_id: Option, ) -> Result { let data: &TransferCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let site_creator_id = blocking(context.pool(), move |conn| { Site::read(conn, 1).map(|s| s.creator_id) diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 1146975f0..bfaceeb02 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -190,7 +190,7 @@ mod tests { use lemmy_api_common::check_validator_time; use lemmy_db_queries::{ establish_unpooled_connection, - source::{local_user::LocalUser_, secret::SecretSingleton}, + source::{local_user::LocalUser_, secret::Secret_}, Crud, }; use lemmy_db_schema::source::{ @@ -198,11 +198,13 @@ mod tests { person::{Person, PersonForm}, secret::Secret, }; - use lemmy_utils::claims::Claims; + use lemmy_utils::{claims::Claims, settings::structs::Settings}; #[test] fn test_should_not_validate_user_token_after_password_change() { let conn = establish_unpooled_connection(); + let secret = Secret::init(&conn).unwrap(); + let settings = Settings::init().unwrap(); let new_person = PersonForm { name: "Gerry9812".into(), @@ -219,9 +221,13 @@ mod tests { let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap(); - let jwt_secret = Secret::get().jwt_secret; - let jwt = Claims::jwt(inserted_local_user.id.0, &jwt_secret).unwrap(); - let claims = Claims::decode(&jwt, jwt_secret.as_ref()).unwrap().claims; + let jwt = Claims::jwt( + inserted_local_user.id.0, + &secret.jwt_secret, + &settings.hostname, + ) + .unwrap(); + let claims = Claims::decode(&jwt, &secret.jwt_secret).unwrap().claims; let check = check_validator_time(&inserted_local_user.validator_time, &claims); assert!(check.is_ok()); diff --git a/crates/api/src/local_user.rs b/crates/api/src/local_user.rs index df03e3020..c69ea48d3 100644 --- a/crates/api/src/local_user.rs +++ b/crates/api/src/local_user.rs @@ -25,7 +25,6 @@ use lemmy_db_queries::{ person_mention::PersonMention_, post::Post_, private_message::PrivateMessage_, - secret::SecretSingleton, }, Blockable, Crud, @@ -44,7 +43,6 @@ use lemmy_db_schema::{ person_mention::*, post::Post, private_message::PrivateMessage, - secret::Secret, site::*, }, }; @@ -63,7 +61,6 @@ use lemmy_utils::{ claims::Claims, email::send_email, location_info, - settings::structs::Settings, utils::{generate_random_string, is_valid_display_name, is_valid_matrix_id, naive_from_unix}, ApiError, ConnectionId, @@ -105,9 +102,12 @@ impl Perform for Login { } // Return the jwt - let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(local_user_view.local_user.id.0, &jwt_secret)?, + jwt: Claims::jwt( + local_user_view.local_user.id.0, + &context.secret().jwt_secret, + &context.settings().hostname, + )?, }) } } @@ -121,7 +121,7 @@ impl Perform for GetCaptcha { context: &Data, _websocket_id: Option, ) -> Result { - let captcha_settings = Settings::get().captcha; + let captcha_settings = context.settings().captcha; if !captcha_settings.enabled { return Ok(GetCaptchaResponse { ok: None }); @@ -167,7 +167,8 @@ impl Perform for SaveUserSettings { _websocket_id: Option, ) -> Result { let data: &SaveUserSettings = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let avatar = diesel_option_overwrite_to_url(&data.avatar)?; let banner = diesel_option_overwrite_to_url(&data.banner)?; @@ -184,7 +185,10 @@ impl Perform for SaveUserSettings { } if let Some(Some(display_name)) = &display_name { - if !is_valid_display_name(display_name.trim()) { + if !is_valid_display_name( + display_name.trim(), + context.settings().actor_name_max_length, + ) { return Err(ApiError::err("invalid_username").into()); } } @@ -271,9 +275,12 @@ impl Perform for SaveUserSettings { }; // Return the jwt - let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?, + jwt: Claims::jwt( + updated_local_user.id.0, + &context.secret().jwt_secret, + &context.settings().hostname, + )?, }) } } @@ -288,7 +295,8 @@ impl Perform for ChangePassword { _websocket_id: Option, ) -> Result { let data: &ChangePassword = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; password_length_check(&data.new_password)?; @@ -315,9 +323,12 @@ impl Perform for ChangePassword { .await??; // Return the jwt - let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?, + jwt: Claims::jwt( + updated_local_user.id.0, + &context.secret().jwt_secret, + &context.settings().hostname, + )?, }) } } @@ -332,7 +343,8 @@ impl Perform for AddAdmin { websocket_id: Option, ) -> Result { let data: &AddAdmin = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Make sure user is an admin is_admin(&local_user_view)?; @@ -394,7 +406,8 @@ impl Perform for BanPerson { websocket_id: Option, ) -> Result { let data: &BanPerson = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Make sure user is an admin is_admin(&local_user_view)?; @@ -486,7 +499,8 @@ impl Perform for BlockPerson { _websocket_id: Option, ) -> Result { let data: &BlockPerson = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let target_id = data.person_id; let person_id = local_user_view.person.id; @@ -539,7 +553,8 @@ impl Perform for GetReplies { _websocket_id: Option, ) -> Result { let data: &GetReplies = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let sort: Option = from_opt_str_to_opt_enum(&data.sort); @@ -576,7 +591,8 @@ impl Perform for GetPersonMentions { _websocket_id: Option, ) -> Result { let data: &GetPersonMentions = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let sort: Option = from_opt_str_to_opt_enum(&data.sort); @@ -610,7 +626,8 @@ impl Perform for MarkPersonMentionAsRead { _websocket_id: Option, ) -> Result { let data: &MarkPersonMentionAsRead = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_mention_id = data.person_mention_id; let read_person_mention = blocking(context.pool(), move |conn| { @@ -653,7 +670,8 @@ impl Perform for MarkAllAsRead { _websocket_id: Option, ) -> Result { let data: &MarkAllAsRead = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.person.id; let replies = blocking(context.pool(), move |conn| { @@ -732,10 +750,16 @@ impl Perform for PasswordReset { // TODO no i18n support here. let email = &local_user_view.local_user.email.expect("email"); let subject = &format!("Password reset for {}", local_user_view.person.name); - let hostname = &Settings::get().get_protocol_and_hostname(); - let html = &format!("

Password Reset Request for {}


Click here to reset your password", local_user_view.person.name, hostname, &token); - send_email(subject, email, &local_user_view.person.name, html) - .map_err(|e| ApiError::err(&e))?; + let protocol_and_hostname = &context.settings().get_protocol_and_hostname(); + let html = &format!("

Password Reset Request for {}


Click here to reset your password", local_user_view.person.name, protocol_and_hostname, &token); + send_email( + subject, + email, + &local_user_view.person.name, + html, + &context.settings(), + ) + .map_err(|e| ApiError::err(&e))?; Ok(PasswordResetResponse {}) } @@ -775,9 +799,12 @@ impl Perform for PasswordChange { .map_err(|_| ApiError::err("couldnt_update_user"))?; // Return the jwt - let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?, + jwt: Claims::jwt( + updated_local_user.id.0, + &context.secret().jwt_secret, + &context.settings().hostname, + )?, }) } } @@ -792,7 +819,8 @@ impl Perform for GetReportCount { websocket_id: Option, ) -> Result { let data: &GetReportCount = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.person.id; let community_id = data.community; diff --git a/crates/api/src/post.rs b/crates/api/src/post.rs index 71b200141..da828725d 100644 --- a/crates/api/src/post.rs +++ b/crates/api/src/post.rs @@ -38,7 +38,8 @@ impl Perform for CreatePostLike { websocket_id: Option, ) -> Result { let data: &CreatePostLike = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Don't do a downvote if site has downvotes disabled check_downvotes_enabled(data.score, context.pool()).await?; @@ -120,7 +121,8 @@ impl Perform for LockPost { websocket_id: Option, ) -> Result { let data: &LockPost = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let post_id = data.post_id; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; @@ -186,7 +188,8 @@ impl Perform for StickyPost { websocket_id: Option, ) -> Result { let data: &StickyPost = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let post_id = data.post_id; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; @@ -256,7 +259,8 @@ impl Perform for SavePost { _websocket_id: Option, ) -> Result { let data: &SavePost = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let post_saved_form = PostSavedForm { post_id: data.post_id, diff --git a/crates/api/src/post_report.rs b/crates/api/src/post_report.rs index e718b775a..e08a64b3f 100644 --- a/crates/api/src/post_report.rs +++ b/crates/api/src/post_report.rs @@ -39,7 +39,8 @@ impl Perform for CreatePostReport { websocket_id: Option, ) -> Result { let data: &CreatePostReport = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // check size of report and check for whitespace let reason = data.reason.trim(); @@ -105,7 +106,8 @@ impl Perform for ResolvePostReport { websocket_id: Option, ) -> Result { let data: &ResolvePostReport = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let report_id = data.report_id; let report = blocking(context.pool(), move |conn| { @@ -157,7 +159,8 @@ impl Perform for ListPostReports { websocket_id: Option, ) -> Result { let data: &ListPostReports = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.person.id; let community_id = data.community; diff --git a/crates/api/src/private_message.rs b/crates/api/src/private_message.rs index f23bf71cd..31f6901dc 100644 --- a/crates/api/src/private_message.rs +++ b/crates/api/src/private_message.rs @@ -20,7 +20,8 @@ impl Perform for MarkPrivateMessageAsRead { websocket_id: Option, ) -> Result { let data: &MarkPrivateMessageAsRead = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Checking permissions let private_message_id = data.private_message_id; diff --git a/crates/api/src/site.rs b/crates/api/src/site.rs index 82c94cbbb..01f7b45f1 100644 --- a/crates/api/src/site.rs +++ b/crates/api/src/site.rs @@ -151,7 +151,8 @@ impl Perform for Search { ) -> Result { let data: &Search = self; - let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?; let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw); let show_bot_accounts = local_user_view @@ -180,7 +181,7 @@ impl Perform for Search { let community_actor_id = data .community_name .as_ref() - .map(|t| build_actor_id_from_shortname(EndpointType::Community, t).ok()) + .map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok()) .unwrap_or(None); let creator_id = data.creator_id; match search_type { @@ -384,7 +385,8 @@ impl Perform for ResolveObject { context: &Data, _websocket_id: Option, ) -> Result { - let local_user_view = get_local_user_view_from_jwt_opt(&self.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt_opt(&self.auth, context.pool(), context.secret()).await?; let res = search_by_apub_id(&self.q, context) .await .map_err(|_| ApiError::err("couldnt_find_object"))?; @@ -443,7 +445,8 @@ impl Perform for TransferSite { _websocket_id: Option, ) -> Result { let data: &TransferSite = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; is_admin(&local_user_view)?; @@ -480,7 +483,12 @@ impl Perform for TransferSite { admins.insert(0, creator_person); let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??; - let federated_instances = build_federated_instances(context.pool()).await?; + let federated_instances = build_federated_instances( + context.pool(), + &context.settings().federation, + &context.settings().hostname, + ) + .await?; Ok(GetSiteResponse { site_view: Some(site_view), @@ -504,7 +512,8 @@ impl Perform for GetSiteConfig { _websocket_id: Option, ) -> Result { let data: &GetSiteConfig = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Only let admins read this is_admin(&local_user_view)?; @@ -525,7 +534,8 @@ impl Perform for SaveSiteConfig { _websocket_id: Option, ) -> Result { let data: &SaveSiteConfig = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Only let admins read this is_admin(&local_user_view)?; diff --git a/crates/api/src/websocket.rs b/crates/api/src/websocket.rs index e69ffdd24..fa03e4ecf 100644 --- a/crates/api/src/websocket.rs +++ b/crates/api/src/websocket.rs @@ -17,7 +17,8 @@ impl Perform for UserJoin { websocket_id: Option, ) -> Result { let data: &UserJoin = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; if let Some(ws_id) = websocket_id { context.chat_server().do_send(JoinUserRoom { diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index 068bd253f..c38d4e620 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -11,7 +11,6 @@ use lemmy_db_queries::{ source::{ community::{CommunityModerator_, Community_}, person_block::PersonBlock_, - secret::SecretSingleton, site::Site_, }, Crud, @@ -42,7 +41,7 @@ use lemmy_db_views_actor::{ use lemmy_utils::{ claims::Claims, email::send_email, - settings::structs::Settings, + settings::structs::{FederationConfig, Settings}, utils::MentionData, ApiError, LemmyError, @@ -73,9 +72,19 @@ pub async fn send_local_notifs( post: Post, pool: &DbPool, do_send_email: bool, + settings: &Settings, ) -> Result, LemmyError> { + let settings = settings.to_owned(); let ids = blocking(pool, move |conn| { - do_send_local_notifs(conn, &mentions, &comment, &person, &post, do_send_email) + do_send_local_notifs( + conn, + &mentions, + &comment, + &person, + &post, + do_send_email, + &settings, + ) }) .await?; @@ -89,13 +98,14 @@ fn do_send_local_notifs( person: &Person, post: &Post, do_send_email: bool, + settings: &Settings, ) -> Vec { let mut recipient_ids = Vec::new(); // Send the local mentions for mention in mentions .iter() - .filter(|m| m.is_local() && m.name.ne(&person.name)) + .filter(|m| m.is_local(&settings.hostname) && m.name.ne(&person.name)) .collect::>() { if let Ok(mention_user_view) = LocalUserView::read_from_name(conn, &mention.name) { @@ -121,6 +131,7 @@ fn do_send_local_notifs( "Mentioned by", "Person Mention", &comment.content, + settings, ) } } @@ -143,6 +154,7 @@ fn do_send_local_notifs( "Reply from", "Comment Reply", &comment.content, + settings, ) } } @@ -161,6 +173,7 @@ fn do_send_local_notifs( "Reply from", "Post Reply", &comment.content, + settings, ) } } @@ -175,6 +188,7 @@ pub fn send_email_to_user( subject_text: &str, body_text: &str, comment_content: &str, + settings: &Settings, ) { if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email { return; @@ -183,18 +197,22 @@ pub fn send_email_to_user( if let Some(user_email) = &local_user_view.local_user.email { let subject = &format!( "{} - {} {}", - subject_text, - Settings::get().hostname, - local_user_view.person.name, + subject_text, settings.hostname, local_user_view.person.name, ); let html = &format!( "

{}


{} - {}

inbox", body_text, local_user_view.person.name, comment_content, - Settings::get().get_protocol_and_hostname() + settings.get_protocol_and_hostname() ); - match send_email(subject, user_email, &local_user_view.person.name, html) { + match send_email( + subject, + user_email, + &local_user_view.person.name, + html, + settings, + ) { Ok(_o) => _o, Err(e) => error!("{}", e), }; @@ -246,9 +264,9 @@ pub async fn mark_post_as_read( pub async fn get_local_user_view_from_jwt( jwt: &str, pool: &DbPool, + secret: &Secret, ) -> Result { - let jwt_secret = Secret::get().jwt_secret; - let claims = Claims::decode(jwt, &jwt_secret) + let claims = Claims::decode(jwt, &secret.jwt_secret) .map_err(|_| ApiError::err("not_logged_in"))? .claims; let local_user_id = LocalUserId(claims.sub); @@ -285,9 +303,10 @@ pub fn check_validator_time( pub async fn get_local_user_view_from_jwt_opt( jwt: &Option, pool: &DbPool, + secret: &Secret, ) -> Result, LemmyError> { match jwt { - Some(jwt) => Ok(Some(get_local_user_view_from_jwt(jwt, pool).await?)), + Some(jwt) => Ok(Some(get_local_user_view_from_jwt(jwt, pool, secret).await?)), None => Ok(None), } } @@ -295,9 +314,9 @@ pub async fn get_local_user_view_from_jwt_opt( pub async fn get_local_user_settings_view_from_jwt( jwt: &str, pool: &DbPool, + secret: &Secret, ) -> Result { - let jwt_secret = Secret::get().jwt_secret; - let claims = Claims::decode(jwt, &jwt_secret) + let claims = Claims::decode(jwt, &secret.jwt_secret) .map_err(|_| ApiError::err("not_logged_in"))? .claims; let local_user_id = LocalUserId(claims.sub); @@ -318,10 +337,11 @@ pub async fn get_local_user_settings_view_from_jwt( pub async fn get_local_user_settings_view_from_jwt_opt( jwt: &Option, pool: &DbPool, + secret: &Secret, ) -> Result, LemmyError> { match jwt { Some(jwt) => Ok(Some( - get_local_user_settings_view_from_jwt(jwt, pool).await?, + get_local_user_settings_view_from_jwt(jwt, pool, secret).await?, )), None => Ok(None), } @@ -391,15 +411,18 @@ pub async fn collect_moderated_communities( pub async fn build_federated_instances( pool: &DbPool, + federation_config: &FederationConfig, + hostname: &str, ) -> Result, LemmyError> { - if Settings::get().federation.enabled { + let federation = federation_config.to_owned(); + if federation.enabled { let distinct_communities = blocking(pool, move |conn| { Community::distinct_federated_communities(conn) }) .await??; - let allowed = Settings::get().federation.allowed_instances; - let blocked = Settings::get().federation.blocked_instances; + let allowed = federation.allowed_instances; + let blocked = federation.blocked_instances; let mut linked = distinct_communities .iter() @@ -411,7 +434,7 @@ pub async fn build_federated_instances( } if let Some(blocked) = blocked.as_ref() { - linked.retain(|a| !blocked.contains(a) && !a.eq(&Settings::get().hostname)); + linked.retain(|a| !blocked.contains(a) && !a.eq(hostname)); } // Sort and remove dupes diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index 766987003..fb175179a 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -40,9 +40,11 @@ impl PerformCrud for CreateComment { websocket_id: Option, ) -> Result { let data: &CreateComment = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; - let content_slurs_removed = remove_slurs(&data.content.to_owned()); + let content_slurs_removed = + remove_slurs(&data.content.to_owned(), &context.settings().slur_regex()); // Check for a community ban let post_id = data.post_id; @@ -91,10 +93,15 @@ impl PerformCrud for CreateComment { // Necessary to update the ap_id let inserted_comment_id = inserted_comment.id; + let protocol_and_hostname = context.settings().get_protocol_and_hostname(); + let updated_comment: Comment = blocking(context.pool(), move |conn| -> Result { - let apub_id = - generate_apub_endpoint(EndpointType::Comment, &inserted_comment_id.to_string())?; + let apub_id = generate_apub_endpoint( + EndpointType::Comment, + &inserted_comment_id.to_string(), + &protocol_and_hostname, + )?; Ok(Comment::update_ap_id(conn, inserted_comment_id, apub_id)?) }) .await? @@ -118,6 +125,7 @@ impl PerformCrud for CreateComment { post, context.pool(), true, + &context.settings(), ) .await?; diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs index 85917846e..0b44f4ebb 100644 --- a/crates/api_crud/src/comment/delete.rs +++ b/crates/api_crud/src/comment/delete.rs @@ -25,7 +25,8 @@ impl PerformCrud for DeleteComment { websocket_id: Option, ) -> Result { let data: &DeleteComment = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let comment_id = data.comment_id; let orig_comment = blocking(context.pool(), move |conn| { @@ -76,6 +77,7 @@ impl PerformCrud for DeleteComment { post, context.pool(), false, + &context.settings(), ) .await?; @@ -102,7 +104,8 @@ impl PerformCrud for RemoveComment { websocket_id: Option, ) -> Result { let data: &RemoveComment = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let comment_id = data.comment_id; let orig_comment = blocking(context.pool(), move |conn| { @@ -169,6 +172,7 @@ impl PerformCrud for RemoveComment { post, context.pool(), false, + &context.settings(), ) .await?; diff --git a/crates/api_crud/src/comment/read.rs b/crates/api_crud/src/comment/read.rs index fc4bd1e35..8ee9eb628 100644 --- a/crates/api_crud/src/comment/read.rs +++ b/crates/api_crud/src/comment/read.rs @@ -17,7 +17,8 @@ impl PerformCrud for GetComments { _websocket_id: Option, ) -> Result { let data: &GetComments = self; - let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?; let show_bot_accounts = local_user_view .as_ref() @@ -31,7 +32,7 @@ impl PerformCrud for GetComments { let community_actor_id = data .community_name .as_ref() - .map(|t| build_actor_id_from_shortname(EndpointType::Community, t).ok()) + .map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok()) .unwrap_or(None); let saved_only = data.saved_only; let page = data.page; diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs index 8a5e8f0ed..24e2ddbdd 100644 --- a/crates/api_crud/src/comment/update.rs +++ b/crates/api_crud/src/comment/update.rs @@ -32,7 +32,8 @@ impl PerformCrud for EditComment { websocket_id: Option, ) -> Result { let data: &EditComment = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let comment_id = data.comment_id; let orig_comment = blocking(context.pool(), move |conn| { @@ -54,7 +55,8 @@ impl PerformCrud for EditComment { } // Do the update - let content_slurs_removed = remove_slurs(&data.content.to_owned()); + let content_slurs_removed = + remove_slurs(&data.content.to_owned(), &context.settings().slur_regex()); let comment_id = data.comment_id; let updated_comment = blocking(context.pool(), move |conn| { Comment::update_content(conn, comment_id, &content_slurs_removed) @@ -81,6 +83,7 @@ impl PerformCrud for EditComment { orig_comment.post, context.pool(), false, + &context.settings(), ) .await?; diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index ecaa9058d..1cc40d85f 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -45,23 +45,28 @@ impl PerformCrud for CreateCommunity { _websocket_id: Option, ) -> Result { let data: &CreateCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let site = blocking(context.pool(), move |conn| Site::read(conn, 0)).await??; if site.community_creation_admin_only && is_admin(&local_user_view).is_err() { return Err(ApiError::err("only_admins_can_create_communities").into()); } - check_slurs(&data.name)?; - check_slurs(&data.title)?; - check_slurs_opt(&data.description)?; + check_slurs(&data.name, &context.settings().slur_regex())?; + check_slurs(&data.title, &context.settings().slur_regex())?; + check_slurs_opt(&data.description, &context.settings().slur_regex())?; - if !is_valid_actor_name(&data.name) { + if !is_valid_actor_name(&data.name, context.settings().actor_name_max_length) { return Err(ApiError::err("invalid_community_name").into()); } // Double check for duplicate community actor_ids - let community_actor_id = generate_apub_endpoint(EndpointType::Community, &data.name)?; + let community_actor_id = generate_apub_endpoint( + EndpointType::Community, + &data.name, + &context.settings().get_protocol_and_hostname(), + )?; let actor_id_cloned = community_actor_id.to_owned(); let community_dupe = blocking(context.pool(), move |conn| { Community::read_from_apub_id(conn, &actor_id_cloned) diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs index b957b7d6e..2d54ff52c 100644 --- a/crates/api_crud/src/community/delete.rs +++ b/crates/api_crud/src/community/delete.rs @@ -21,7 +21,8 @@ impl PerformCrud for DeleteCommunity { websocket_id: Option, ) -> Result { let data: &DeleteCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Fetch the community mods let community_id = data.community_id; @@ -75,7 +76,8 @@ impl PerformCrud for RemoveCommunity { websocket_id: Option, ) -> Result { let data: &RemoveCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Verify its an admin (only an admin can remove a community) is_admin(&local_user_view)?; diff --git a/crates/api_crud/src/community/read.rs b/crates/api_crud/src/community/read.rs index 94e4ce7f4..b29f5f0a0 100644 --- a/crates/api_crud/src/community/read.rs +++ b/crates/api_crud/src/community/read.rs @@ -27,14 +27,16 @@ impl PerformCrud for GetCommunity { _websocket_id: Option, ) -> Result { let data: &GetCommunity = self; - let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.map(|u| u.person.id); let community_id = match data.id { Some(id) => id, None => { let name = data.name.to_owned().unwrap_or_else(|| "main".to_string()); - let community_actor_id = build_actor_id_from_shortname(EndpointType::Community, &name)?; + let community_actor_id = + build_actor_id_from_shortname(EndpointType::Community, &name, &context.settings())?; blocking(context.pool(), move |conn| { Community::read_from_apub_id(conn, &community_actor_id) @@ -89,7 +91,8 @@ impl PerformCrud for ListCommunities { _websocket_id: Option, ) -> Result { let data: &ListCommunities = self; - let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.to_owned().map(|l| l.person.id); diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index 2196c57b7..ef94ad595 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -26,10 +26,11 @@ impl PerformCrud for EditCommunity { websocket_id: Option, ) -> Result { let data: &EditCommunity = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; - check_slurs_opt(&data.title)?; - check_slurs_opt(&data.description)?; + check_slurs_opt(&data.title, &context.settings().slur_regex())?; + check_slurs_opt(&data.description, &context.settings().slur_regex())?; // Verify its a mod (only mods can edit it) let community_id = data.community_id; diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index e21c6f33d..11896875c 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -38,10 +38,12 @@ impl PerformCrud for CreatePost { websocket_id: Option, ) -> Result { let data: &CreatePost = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; - check_slurs(&data.name)?; - check_slurs_opt(&data.body)?; + let slur_regex = &context.settings().slur_regex(); + check_slurs(&data.name, slur_regex)?; + check_slurs_opt(&data.body, slur_regex)?; if !is_valid_post_title(&data.name) { return Err(ApiError::err("invalid_post_title").into()); @@ -51,7 +53,8 @@ impl PerformCrud for CreatePost { // Fetch post links and pictrs cached image let data_url = data.url.as_ref(); - let (metadata_res, pictrs_thumbnail) = fetch_site_data(context.client(), data_url).await; + let (metadata_res, pictrs_thumbnail) = + fetch_site_data(context.client(), &context.settings(), data_url).await; let (embed_title, embed_description, embed_html) = metadata_res .map(|u| (u.title, u.description, u.html)) .unwrap_or((None, None, None)); @@ -85,8 +88,13 @@ impl PerformCrud for CreatePost { }; let inserted_post_id = inserted_post.id; + let protocol_and_hostname = context.settings().get_protocol_and_hostname(); let updated_post = blocking(context.pool(), move |conn| -> Result { - let apub_id = generate_apub_endpoint(EndpointType::Post, &inserted_post_id.to_string())?; + let apub_id = generate_apub_endpoint( + EndpointType::Post, + &inserted_post_id.to_string(), + &protocol_and_hostname, + )?; Ok(Post::update_ap_id(conn, inserted_post_id, apub_id)?) }) .await? diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs index c0ef35122..18ede8a37 100644 --- a/crates/api_crud/src/post/delete.rs +++ b/crates/api_crud/src/post/delete.rs @@ -23,7 +23,8 @@ impl PerformCrud for DeletePost { websocket_id: Option, ) -> Result { let data: &DeletePost = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let post_id = data.post_id; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; @@ -83,7 +84,8 @@ impl PerformCrud for RemovePost { websocket_id: Option, ) -> Result { let data: &RemovePost = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let post_id = data.post_id; let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs index d651a0577..1cfcbaf8c 100644 --- a/crates/api_crud/src/post/read.rs +++ b/crates/api_crud/src/post/read.rs @@ -24,7 +24,8 @@ impl PerformCrud for GetPost { _websocket_id: Option, ) -> Result { let data: &GetPost = self; - let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?; let show_bot_accounts = local_user_view .as_ref() @@ -112,7 +113,8 @@ impl PerformCrud for GetPosts { _websocket_id: Option, ) -> Result { let data: &GetPosts = self; - let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.to_owned().map(|l| l.person.id); @@ -133,7 +135,7 @@ impl PerformCrud for GetPosts { let community_actor_id = data .community_name .as_ref() - .map(|t| build_actor_id_from_shortname(EndpointType::Community, t).ok()) + .map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok()) .unwrap_or(None); let saved_only = data.saved_only; diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index d52538e06..b94786826 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -23,10 +23,12 @@ impl PerformCrud for EditPost { websocket_id: Option, ) -> Result { let data: &EditPost = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; - check_slurs_opt(&data.name)?; - check_slurs_opt(&data.body)?; + let slur_regex = &context.settings().slur_regex(); + check_slurs_opt(&data.name, slur_regex)?; + check_slurs_opt(&data.body, slur_regex)?; if let Some(name) = &data.name { if !is_valid_post_title(name) { @@ -51,7 +53,8 @@ impl PerformCrud for EditPost { // Fetch post links and Pictrs cached image let data_url = data.url.as_ref(); - let (metadata_res, pictrs_thumbnail) = fetch_site_data(context.client(), data_url).await; + let (metadata_res, pictrs_thumbnail) = + fetch_site_data(context.client(), &context.settings(), data_url).await; let (embed_title, embed_description, embed_html) = metadata_res .map(|u| (u.title, u.description, u.html)) .unwrap_or((None, None, None)); diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index 18ef3835d..10d77da45 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -31,9 +31,11 @@ impl PerformCrud for CreatePrivateMessage { websocket_id: Option, ) -> Result { let data: &CreatePrivateMessage = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; - let content_slurs_removed = remove_slurs(&data.content.to_owned()); + let content_slurs_removed = + remove_slurs(&data.content.to_owned(), &context.settings().slur_regex()); check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?; @@ -56,12 +58,14 @@ impl PerformCrud for CreatePrivateMessage { }; let inserted_private_message_id = inserted_private_message.id; + let protocol_and_hostname = context.settings().get_protocol_and_hostname(); let updated_private_message = blocking( context.pool(), move |conn| -> Result { let apub_id = generate_apub_endpoint( EndpointType::PrivateMessage, &inserted_private_message_id.to_string(), + &protocol_and_hostname, )?; Ok(PrivateMessage::update_ap_id( conn, @@ -101,6 +105,7 @@ impl PerformCrud for CreatePrivateMessage { "Private Message from", "Private Message", &content_slurs_removed, + &context.settings(), ); } diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs index 6cd2b44f2..2171a1436 100644 --- a/crates/api_crud/src/private_message/delete.rs +++ b/crates/api_crud/src/private_message/delete.rs @@ -24,7 +24,8 @@ impl PerformCrud for DeletePrivateMessage { websocket_id: Option, ) -> Result { let data: &DeletePrivateMessage = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Checking permissions let private_message_id = data.private_message_id; diff --git a/crates/api_crud/src/private_message/read.rs b/crates/api_crud/src/private_message/read.rs index 3acbaa392..bf119d75c 100644 --- a/crates/api_crud/src/private_message/read.rs +++ b/crates/api_crud/src/private_message/read.rs @@ -20,7 +20,8 @@ impl PerformCrud for GetPrivateMessages { _websocket_id: Option, ) -> Result { let data: &GetPrivateMessages = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.person.id; let page = data.page; diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs index 72ca4fbbf..1f7ec82ba 100644 --- a/crates/api_crud/src/private_message/update.rs +++ b/crates/api_crud/src/private_message/update.rs @@ -24,7 +24,8 @@ impl PerformCrud for EditPrivateMessage { websocket_id: Option, ) -> Result { let data: &EditPrivateMessage = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Checking permissions let private_message_id = data.private_message_id; @@ -37,7 +38,7 @@ impl PerformCrud for EditPrivateMessage { } // Doing the update - let content_slurs_removed = remove_slurs(&data.content); + let content_slurs_removed = remove_slurs(&data.content, &context.settings().slur_regex()); let private_message_id = data.private_message_id; let updated_private_message = blocking(context.pool(), move |conn| { PrivateMessage::update_content(conn, private_message_id, &content_slurs_removed) diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs index b84adcdb4..8038f62cb 100644 --- a/crates/api_crud/src/site/create.rs +++ b/crates/api_crud/src/site/create.rs @@ -39,10 +39,11 @@ impl PerformCrud for CreateSite { return Err(ApiError::err("site_already_exists").into()); }; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; - check_slurs(&data.name)?; - check_slurs_opt(&data.description)?; + check_slurs(&data.name, &context.settings().slur_regex())?; + check_slurs_opt(&data.description, &context.settings().slur_regex())?; // Make sure user is an admin is_admin(&local_user_view)?; diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs index 0478109dc..1bed1e3bc 100644 --- a/crates/api_crud/src/site/read.rs +++ b/crates/api_crud/src/site/read.rs @@ -15,7 +15,7 @@ use lemmy_db_views_actor::{ person_block_view::PersonBlockView, person_view::PersonViewSafe, }; -use lemmy_utils::{settings::structs::Settings, version, ApiError, ConnectionId, LemmyError}; +use lemmy_utils::{version, ApiError, ConnectionId, LemmyError}; use lemmy_websocket::{messages::GetUsersOnline, LemmyContext}; use log::info; @@ -34,7 +34,7 @@ impl PerformCrud for GetSite { Ok(site_view) => Some(site_view), // If the site isn't created yet, check the setup Err(_) => { - if let Some(setup) = Settings::get().setup.as_ref() { + if let Some(setup) = context.settings().setup.as_ref() { let register = Register { username: setup.admin_username.to_owned(), email: setup.admin_email.to_owned(), @@ -91,7 +91,8 @@ impl PerformCrud for GetSite { // Build the local user let my_user = if let Some(local_user_view) = - get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool()).await? + get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool(), context.secret()) + .await? { let person_id = local_user_view.person.id; let follows = blocking(context.pool(), move |conn| { @@ -131,7 +132,12 @@ impl PerformCrud for GetSite { None }; - let federated_instances = build_federated_instances(context.pool()).await?; + let federated_instances = build_federated_instances( + context.pool(), + &context.settings().federation, + &context.settings().hostname, + ) + .await?; Ok(GetSiteResponse { site_view, diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs index 781ea0081..b92cfd4a1 100644 --- a/crates/api_crud/src/site/update.rs +++ b/crates/api_crud/src/site/update.rs @@ -30,10 +30,11 @@ impl PerformCrud for EditSite { websocket_id: Option, ) -> Result { let data: &EditSite = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; - check_slurs_opt(&data.name)?; - check_slurs_opt(&data.description)?; + check_slurs_opt(&data.name, &context.settings().slur_regex())?; + check_slurs_opt(&data.description, &context.settings().slur_regex())?; // Make sure user is an admin is_admin(&local_user_view)?; diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index 24b734623..2b093e013 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -9,7 +9,7 @@ use lemmy_apub::{ EndpointType, }; use lemmy_db_queries::{ - source::{local_user::LocalUser_, secret::SecretSingleton, site::Site_}, + source::{local_user::LocalUser_, site::Site_}, Crud, Followable, Joinable, @@ -21,7 +21,6 @@ use lemmy_db_schema::{ community::*, local_user::{LocalUser, LocalUserForm}, person::*, - secret::Secret, site::*, }, CommunityId, @@ -30,7 +29,6 @@ use lemmy_db_views_actor::person_view::PersonViewSafe; use lemmy_utils::{ apub::generate_actor_keypair, claims::Claims, - settings::structs::Settings, utils::{check_slurs, is_valid_actor_name}, ApiError, ConnectionId, @@ -70,7 +68,7 @@ impl PerformCrud for Register { .await??; // If its not the admin, check the captcha - if !no_admins && Settings::get().captcha.enabled { + if !no_admins && context.settings().captcha.enabled { let check = context .chat_server() .send(CheckCaptcha { @@ -89,13 +87,17 @@ impl PerformCrud for Register { } } - check_slurs(&data.username)?; + check_slurs(&data.username, &context.settings().slur_regex())?; let actor_keypair = generate_actor_keypair()?; - if !is_valid_actor_name(&data.username) { + if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) { return Err(ApiError::err("invalid_username").into()); } - let actor_id = generate_apub_endpoint(EndpointType::Person, &data.username)?; + let actor_id = generate_apub_endpoint( + EndpointType::Person, + &data.username, + &context.settings().get_protocol_and_hostname(), + )?; // We have to create both a person, and local_user @@ -165,6 +167,7 @@ impl PerformCrud for Register { let main_community_keypair = generate_actor_keypair()?; // Create the main community if it doesn't exist + let protocol_and_hostname = context.settings().get_protocol_and_hostname(); let main_community = match blocking(context.pool(), move |conn| { Community::read(conn, CommunityId(2)) }) @@ -173,7 +176,11 @@ impl PerformCrud for Register { Ok(c) => c, Err(_e) => { let default_community_name = "main"; - let actor_id = generate_apub_endpoint(EndpointType::Community, default_community_name)?; + let actor_id = generate_apub_endpoint( + EndpointType::Community, + default_community_name, + &protocol_and_hostname, + )?; let community_form = CommunityForm { name: default_community_name.to_string(), title: "The Default Community".to_string(), @@ -219,9 +226,12 @@ impl PerformCrud for Register { } // Return the jwt - let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(inserted_local_user.id.0, &jwt_secret)?, + jwt: Claims::jwt( + inserted_local_user.id.0, + &context.secret().jwt_secret, + &context.settings().hostname, + )?, }) } } diff --git a/crates/api_crud/src/user/delete.rs b/crates/api_crud/src/user/delete.rs index fb9480dc7..24f3c4003 100644 --- a/crates/api_crud/src/user/delete.rs +++ b/crates/api_crud/src/user/delete.rs @@ -17,7 +17,8 @@ impl PerformCrud for DeleteAccount { _websocket_id: Option, ) -> Result { let data: &DeleteAccount = self; - let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; // Verify the password let valid: bool = verify( diff --git a/crates/api_crud/src/user/read.rs b/crates/api_crud/src/user/read.rs index 4cfd90ab5..b029f2998 100644 --- a/crates/api_crud/src/user/read.rs +++ b/crates/api_crud/src/user/read.rs @@ -22,7 +22,8 @@ impl PerformCrud for GetPersonDetails { _websocket_id: Option, ) -> Result { let data: &GetPersonDetails = self; - let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?; + let local_user_view = + get_local_user_view_from_jwt_opt(&data.auth, context.pool(), context.secret()).await?; let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw); let show_bot_accounts = local_user_view @@ -41,7 +42,8 @@ impl PerformCrud for GetPersonDetails { .username .to_owned() .unwrap_or_else(|| "admin".to_string()); - let actor_id = build_actor_id_from_shortname(EndpointType::Person, &name)?; + let actor_id = + build_actor_id_from_shortname(EndpointType::Person, &name, &context.settings())?; let person = blocking(context.pool(), move |conn| { Person::read_from_apub_id(conn, &actor_id) diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index 9ce172004..130623ff7 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -57,7 +57,10 @@ impl CreateOrUpdateComment { }) .await??; - let id = generate_activity_id(kind.clone())?; + let id = generate_activity_id( + kind.clone(), + &context.settings().get_protocol_and_hostname(), + )?; let maa = collect_non_local_mentions(comment, &community, context).await?; let create_or_update = CreateOrUpdateComment { @@ -87,7 +90,7 @@ impl ActivityHandler for CreateOrUpdateComment { let community = extract_community(&self.cc, context, request_counter).await?; let community_id = ObjectId::new(community.actor_id()); - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &community_id, context, request_counter).await?; verify_domains_match(self.actor.inner(), self.object.id_unchecked())?; // TODO: should add a check that the correct community is in cc (probably needs changes to diff --git a/crates/apub/src/activities/comment/mod.rs b/crates/apub/src/activities/comment/mod.rs index 8f27e9bac..9e91d2f1b 100644 --- a/crates/apub/src/activities/comment/mod.rs +++ b/crates/apub/src/activities/comment/mod.rs @@ -14,13 +14,11 @@ use lemmy_db_schema::{ }; use lemmy_utils::{ request::{retry, RecvError}, - settings::structs::Settings, utils::{scrape_text_for_mentions, MentionData}, LemmyError, }; use lemmy_websocket::LemmyContext; use log::debug; -use reqwest::Client; use url::Url; pub mod create_or_update; @@ -41,7 +39,16 @@ async fn get_notif_recipients( // anyway. // TODO: for compatibility with other projects, it would be much better to read this from cc or tags let mentions = scrape_text_for_mentions(&comment.content); - send_local_notifs(mentions, comment.clone(), actor, post, context.pool(), true).await + send_local_notifs( + mentions, + comment.clone(), + actor, + post, + context.pool(), + true, + &context.settings(), + ) + .await } pub struct MentionsAndAddresses { @@ -70,12 +77,12 @@ pub async fn collect_non_local_mentions( let mentions = scrape_text_for_mentions(&comment.content) .into_iter() // Filter only the non-local ones - .filter(|m| !m.is_local()) + .filter(|m| !m.is_local(&context.settings().hostname)) .collect::>(); for mention in &mentions { // TODO should it be fetching it every time? - if let Ok(actor_id) = fetch_webfinger_url(mention, context.client()).await { + if let Ok(actor_id) = fetch_webfinger_url(mention, context).await { let actor_id: ObjectId = ObjectId::new(actor_id); debug!("mention actor_id: {}", actor_id); addressed_ccs.push(actor_id.to_owned().to_string().parse()?); @@ -120,17 +127,20 @@ async fn get_comment_parent_creator( /// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`, /// using webfinger. -async fn fetch_webfinger_url(mention: &MentionData, client: &Client) -> Result { +async fn fetch_webfinger_url( + mention: &MentionData, + context: &LemmyContext, +) -> Result { let fetch_url = format!( "{}://{}/.well-known/webfinger?resource=acct:{}@{}", - Settings::get().get_protocol_string(), + context.settings().get_protocol_string(), mention.domain, mention.name, mention.domain ); debug!("Fetching webfinger url: {}", &fetch_url); - let response = retry(|| client.get(&fetch_url).send()).await?; + let response = retry(|| context.client().get(&fetch_url).send()).await?; let res: WebfingerResponse = response .json() diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index a066211f3..a71414dd2 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -55,7 +55,10 @@ impl AddMod { actor: &Person, context: &LemmyContext, ) -> Result<(), LemmyError> { - let id = generate_activity_id(AddType::Add)?; + let id = generate_activity_id( + AddType::Add, + &context.settings().get_protocol_and_hostname(), + )?; let add = AddMod { actor: ObjectId::new(actor.actor_id()), to: [PublicUrl::Public], @@ -81,7 +84,7 @@ impl ActivityHandler for AddMod { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; verify_add_remove_moderator_target(&self.target, &self.cc[0])?; diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 96797c8ff..dae37b60e 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -84,7 +84,10 @@ impl AnnounceActivity { object, cc: vec![community.followers_url()], kind: AnnounceType::Announce, - id: generate_activity_id(&AnnounceType::Announce)?, + id: generate_activity_id( + &AnnounceType::Announce, + &context.settings().get_protocol_and_hostname(), + )?, context: lemmy_context(), unparsed: Default::default(), }; @@ -100,7 +103,7 @@ impl ActivityHandler for AnnounceActivity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index f7e81f97c..ad9999884 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -56,6 +56,7 @@ impl BlockUserFromCommunity { community: &Community, target: &Person, actor: &Person, + context: &LemmyContext, ) -> Result { Ok(BlockUserFromCommunity { actor: ObjectId::new(actor.actor_id()), @@ -63,7 +64,10 @@ impl BlockUserFromCommunity { object: ObjectId::new(target.actor_id()), cc: [ObjectId::new(community.actor_id())], kind: BlockType::Block, - id: generate_activity_id(BlockType::Block)?, + id: generate_activity_id( + BlockType::Block, + &context.settings().get_protocol_and_hostname(), + )?, context: lemmy_context(), unparsed: Default::default(), }) @@ -75,7 +79,7 @@ impl BlockUserFromCommunity { actor: &Person, context: &LemmyContext, ) -> Result<(), LemmyError> { - let block = BlockUserFromCommunity::new(community, target, actor)?; + let block = BlockUserFromCommunity::new(community, target, actor, context)?; let block_id = block.id.clone(); let activity = AnnouncableActivities::BlockUserFromCommunity(block); @@ -91,7 +95,7 @@ impl ActivityHandler for BlockUserFromCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; Ok(()) diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index ba80ff4ad..642f83645 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -1,7 +1,7 @@ use crate::{check_is_apub_id_valid, CommunityType}; use itertools::Itertools; use lemmy_db_schema::source::community::Community; -use lemmy_utils::{settings::structs::Settings, LemmyError}; +use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; @@ -19,14 +19,16 @@ async fn list_community_follower_inboxes( ) -> Result, LemmyError> { Ok( vec![ - community.get_follower_inboxes(context.pool()).await?, + community + .get_follower_inboxes(context.pool(), &context.settings()) + .await?, additional_inboxes, ] .iter() .flatten() .unique() - .filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname)) - .filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok()) + .filter(|inbox| inbox.host_str() != Some(&context.settings().hostname)) + .filter(|inbox| check_is_apub_id_valid(inbox, false, &context.settings()).is_ok()) .map(|inbox| inbox.to_owned()) .collect(), ) diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index 4960076c1..2da9bbdbb 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -57,7 +57,10 @@ impl RemoveMod { actor: &Person, context: &LemmyContext, ) -> Result<(), LemmyError> { - let id = generate_activity_id(RemoveType::Remove)?; + let id = generate_activity_id( + RemoveType::Remove, + &context.settings().get_protocol_and_hostname(), + )?; let remove = RemoveMod { actor: ObjectId::new(actor.actor_id()), to: [PublicUrl::Public], @@ -83,7 +86,7 @@ impl ActivityHandler for RemoveMod { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; if let Some(target) = &self.target { verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index eec906821..29aa83f5b 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -52,9 +52,12 @@ impl UndoBlockUserFromCommunity { actor: &Person, context: &LemmyContext, ) -> Result<(), LemmyError> { - let block = BlockUserFromCommunity::new(community, target, actor)?; + let block = BlockUserFromCommunity::new(community, target, actor, context)?; - let id = generate_activity_id(UndoType::Undo)?; + let id = generate_activity_id( + UndoType::Undo, + &context.settings().get_protocol_and_hostname(), + )?; let undo = UndoBlockUserFromCommunity { actor: ObjectId::new(actor.actor_id()), to: [PublicUrl::Public], @@ -79,7 +82,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; self.object.verify(context, request_counter).await?; diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index a6da1c9f3..d217e2561 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -55,7 +55,10 @@ impl UpdateCommunity { actor: &Person, context: &LemmyContext, ) -> Result<(), LemmyError> { - let id = generate_activity_id(UpdateType::Update)?; + let id = generate_activity_id( + UpdateType::Update, + &context.settings().get_protocol_and_hostname(), + )?; let update = UpdateCommunity { actor: ObjectId::new(actor.actor_id()), to: [PublicUrl::Public], @@ -79,7 +82,7 @@ impl ActivityHandler for UpdateCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; Ok(()) @@ -96,8 +99,12 @@ impl ActivityHandler for UpdateCommunity { }) .await??; - let updated_community = - Group::from_apub_to_form(&self.object, &community.actor_id.clone().into()).await?; + let updated_community = Group::from_apub_to_form( + &self.object, + &community.actor_id.clone().into(), + &context.settings(), + ) + .await?; let cf = CommunityForm { name: updated_community.name, title: updated_community.title, diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 8e8bd942f..9e957a8ee 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -87,7 +87,7 @@ impl ActivityHandler for Delete { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_delete_activity( &self.object, self, @@ -138,6 +138,7 @@ impl Delete { community: &Community, object_id: Url, summary: Option, + context: &LemmyContext, ) -> Result { Ok(Delete { actor: ObjectId::new(actor.actor_id()), @@ -146,7 +147,10 @@ impl Delete { cc: [ObjectId::new(community.actor_id())], kind: DeleteType::Delete, summary, - id: generate_activity_id(DeleteType::Delete)?, + id: generate_activity_id( + DeleteType::Delete, + &context.settings().get_protocol_and_hostname(), + )?, context: lemmy_context(), unparsed: Default::default(), }) @@ -158,7 +162,7 @@ impl Delete { summary: Option, context: &LemmyContext, ) -> Result<(), LemmyError> { - let delete = Delete::new(actor, community, object_id, summary)?; + let delete = Delete::new(actor, community, object_id, summary, context)?; let delete_id = delete.id.clone(); let activity = AnnouncableActivities::Delete(delete); diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 2dbbf9c46..f74c34c90 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -59,7 +59,7 @@ impl ActivityHandler for UndoDelete { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; self.object.verify(context, request_counter).await?; verify_delete_activity( &self.object.object, @@ -106,9 +106,12 @@ impl UndoDelete { summary: Option, context: &LemmyContext, ) -> Result<(), LemmyError> { - let object = Delete::new(actor, community, object_id, summary)?; + let object = Delete::new(actor, community, object_id, summary, context)?; - let id = generate_activity_id(UndoType::Undo)?; + let id = generate_activity_id( + UndoType::Undo, + &context.settings().get_protocol_and_hostname(), + )?; let undo = UndoDelete { actor: ObjectId::new(actor.actor_id()), to: [PublicUrl::Public], diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index a7472bc1c..86b0d2093 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -61,7 +61,10 @@ impl AcceptFollowCommunity { to: ObjectId::new(person.actor_id()), object: follow, kind: AcceptType::Accept, - id: generate_activity_id(AcceptType::Accept)?, + id: generate_activity_id( + AcceptType::Accept, + &context.settings().get_protocol_and_hostname(), + )?, context: lemmy_context(), unparsed: Default::default(), }; @@ -77,7 +80,7 @@ impl ActivityHandler for AcceptFollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_urls_match(self.to.inner(), self.object.actor())?; verify_urls_match(self.actor(), self.object.to.inner())?; verify_community(&self.actor, context, request_counter).await?; diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 8af3d3889..21446eb12 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -48,13 +48,17 @@ impl FollowCommunity { pub(in crate::activities::following) fn new( actor: &Person, community: &Community, + context: &LemmyContext, ) -> Result { Ok(FollowCommunity { actor: ObjectId::new(actor.actor_id()), to: ObjectId::new(community.actor_id()), object: ObjectId::new(community.actor_id()), kind: FollowType::Follow, - id: generate_activity_id(FollowType::Follow)?, + id: generate_activity_id( + FollowType::Follow, + &context.settings().get_protocol_and_hostname(), + )?, context: lemmy_context(), unparsed: Default::default(), }) @@ -74,7 +78,7 @@ impl FollowCommunity { }) .await?; - let follow = FollowCommunity::new(actor, community)?; + let follow = FollowCommunity::new(actor, community, context)?; let inbox = vec![community.inbox_url.clone().into()]; send_activity_new(context, &follow, &follow.id, actor, inbox, true).await } @@ -87,7 +91,7 @@ impl ActivityHandler for FollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_urls_match(self.to.inner(), self.object.inner())?; verify_person(&self.actor, context, request_counter).await?; Ok(()) diff --git a/crates/apub/src/activities/following/undo.rs b/crates/apub/src/activities/following/undo.rs index f35b3095f..cc103075d 100644 --- a/crates/apub/src/activities/following/undo.rs +++ b/crates/apub/src/activities/following/undo.rs @@ -49,13 +49,16 @@ impl UndoFollowCommunity { community: &Community, context: &LemmyContext, ) -> Result<(), LemmyError> { - let object = FollowCommunity::new(actor, community)?; + let object = FollowCommunity::new(actor, community, context)?; let undo = UndoFollowCommunity { actor: ObjectId::new(actor.actor_id()), to: ObjectId::new(community.actor_id()), object, kind: UndoType::Undo, - id: generate_activity_id(UndoType::Undo)?, + id: generate_activity_id( + UndoType::Undo, + &context.settings().get_protocol_and_hostname(), + )?, context: lemmy_context(), unparsed: Default::default(), }; @@ -71,7 +74,7 @@ impl ActivityHandler for UndoFollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_urls_match(self.to.inner(), self.object.object.inner())?; verify_urls_match(self.actor(), self.object.actor())?; verify_person(&self.actor, context, request_counter).await?; diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 3cfaca859..e72f1e1dc 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -91,8 +91,8 @@ async fn verify_community( Ok(()) } -fn verify_activity(activity: &dyn ActivityFields) -> Result<(), LemmyError> { - check_is_apub_id_valid(activity.actor(), false)?; +fn verify_activity(activity: &dyn ActivityFields, settings: &Settings) -> Result<(), LemmyError> { + check_is_apub_id_valid(activity.actor(), false, settings)?; verify_domains_match(activity.id_unchecked(), activity.actor())?; Ok(()) } @@ -146,13 +146,13 @@ fn verify_add_remove_moderator_target( /// Generate a unique ID for an activity, in the format: /// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36` -fn generate_activity_id(kind: T) -> Result +fn generate_activity_id(kind: T, protocol_and_hostname: &str) -> Result where T: ToString, { let id = format!( "{}/activities/{}/{}", - Settings::get().get_protocol_and_hostname(), + protocol_and_hostname, kind.to_string().to_lowercase(), Uuid::new_v4() ); diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index 8a48da5f8..ae93f0988 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -59,7 +59,10 @@ impl CreateOrUpdatePost { }) .await??; - let id = generate_activity_id(kind.clone())?; + let id = generate_activity_id( + kind.clone(), + &context.settings().get_protocol_and_hostname(), + )?; let create_or_update = CreateOrUpdatePost { actor: ObjectId::new(actor.actor_id()), to: [PublicUrl::Public], @@ -83,7 +86,7 @@ impl ActivityHandler for CreateOrUpdatePost { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; let community = self.cc[0].dereference(context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; match self.kind { diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index 83f4824b1..f3b1c91fb 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -42,7 +42,10 @@ impl CreateOrUpdatePrivateMessage { let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; - let id = generate_activity_id(kind.clone())?; + let id = generate_activity_id( + kind.clone(), + &context.settings().get_protocol_and_hostname(), + )?; let create_or_update = CreateOrUpdatePrivateMessage { context: lemmy_context(), id: id.clone(), @@ -63,7 +66,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person(&self.actor, context, request_counter).await?; verify_domains_match(self.actor.inner(), self.object.id_unchecked())?; self.object.verify(context, request_counter).await?; diff --git a/crates/apub/src/activities/private_message/delete.rs b/crates/apub/src/activities/private_message/delete.rs index 82aad3177..bf12f9d1e 100644 --- a/crates/apub/src/activities/private_message/delete.rs +++ b/crates/apub/src/activities/private_message/delete.rs @@ -39,13 +39,17 @@ impl DeletePrivateMessage { pub(in crate::activities::private_message) fn new( actor: &Person, pm: &PrivateMessage, + context: &LemmyContext, ) -> Result { Ok(DeletePrivateMessage { actor: ObjectId::new(actor.actor_id()), to: ObjectId::new(actor.actor_id()), object: pm.ap_id.clone().into(), kind: DeleteType::Delete, - id: generate_activity_id(DeleteType::Delete)?, + id: generate_activity_id( + DeleteType::Delete, + &context.settings().get_protocol_and_hostname(), + )?, context: lemmy_context(), unparsed: Default::default(), }) @@ -55,7 +59,7 @@ impl DeletePrivateMessage { pm: &PrivateMessage, context: &LemmyContext, ) -> Result<(), LemmyError> { - let delete = DeletePrivateMessage::new(actor, pm)?; + let delete = DeletePrivateMessage::new(actor, pm, context)?; let delete_id = delete.id.clone(); let recipient_id = pm.recipient_id; @@ -73,7 +77,7 @@ impl ActivityHandler for DeletePrivateMessage { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person(&self.actor, context, request_counter).await?; verify_domains_match(self.actor.inner(), &self.object)?; Ok(()) diff --git a/crates/apub/src/activities/private_message/undo_delete.rs b/crates/apub/src/activities/private_message/undo_delete.rs index 2dc9d7242..5aa6dbed4 100644 --- a/crates/apub/src/activities/private_message/undo_delete.rs +++ b/crates/apub/src/activities/private_message/undo_delete.rs @@ -50,8 +50,11 @@ impl UndoDeletePrivateMessage { let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; - let object = DeletePrivateMessage::new(actor, pm)?; - let id = generate_activity_id(UndoType::Undo)?; + let object = DeletePrivateMessage::new(actor, pm, context)?; + let id = generate_activity_id( + UndoType::Undo, + &context.settings().get_protocol_and_hostname(), + )?; let undo = UndoDeletePrivateMessage { actor: ObjectId::new(actor.actor_id()), to: ObjectId::new(recipient.actor_id()), @@ -73,7 +76,7 @@ impl ActivityHandler for UndoDeletePrivateMessage { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person(&self.actor, context, request_counter).await?; verify_urls_match(self.actor(), self.object.actor())?; verify_domains_match(self.actor(), &self.object.object)?; diff --git a/crates/apub/src/activities/send/community.rs b/crates/apub/src/activities/send/community.rs index 29aa9dba8..7f9c19e8c 100644 --- a/crates/apub/src/activities/send/community.rs +++ b/crates/apub/src/activities/send/community.rs @@ -4,7 +4,7 @@ use lemmy_api_common::blocking; use lemmy_db_queries::DbPool; use lemmy_db_schema::source::community::Community; use lemmy_db_views_actor::community_follower_view::CommunityFollowerView; -use lemmy_utils::LemmyError; +use lemmy_utils::{settings::structs::Settings, LemmyError}; use url::Url; impl ActorType for Community { @@ -40,7 +40,11 @@ impl CommunityType for Community { } /// For a given community, returns the inboxes of all followers. - async fn get_follower_inboxes(&self, pool: &DbPool) -> Result, LemmyError> { + async fn get_follower_inboxes( + &self, + pool: &DbPool, + settings: &Settings, + ) -> Result, LemmyError> { let id = self.id; let follows = blocking(pool, move |conn| { @@ -54,7 +58,7 @@ impl CommunityType for Community { .map(|i| i.into_inner()) .unique() // Don't send to blocked instances - .filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok()) + .filter(|inbox| check_is_apub_id_valid(inbox, false, settings).is_ok()) .collect(); Ok(inboxes) diff --git a/crates/apub/src/activities/undo_remove.rs b/crates/apub/src/activities/undo_remove.rs index eaf3684de..422d0deea 100644 --- a/crates/apub/src/activities/undo_remove.rs +++ b/crates/apub/src/activities/undo_remove.rs @@ -43,7 +43,7 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; self.object.verify(context, request_counter).await?; verify_delete_activity( diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index e11d29602..963292fa5 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -64,8 +64,11 @@ impl UndoVote { }) .await??; - let object = Vote::new(object, actor, &community, kind.clone())?; - let id = generate_activity_id(UndoType::Undo)?; + let object = Vote::new(object, actor, &community, kind.clone(), context)?; + let id = generate_activity_id( + UndoType::Undo, + &context.settings().get_protocol_and_hostname(), + )?; let undo_vote = UndoVote { actor: ObjectId::new(actor.actor_id()), to: [PublicUrl::Public], @@ -88,7 +91,7 @@ impl ActivityHandler for UndoVote { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; verify_urls_match(self.actor(), self.object.actor())?; self.object.verify(context, request_counter).await?; diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 69a9c3ba9..e74dea108 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -77,6 +77,7 @@ impl Vote { actor: &Person, community: &Community, kind: VoteType, + context: &LemmyContext, ) -> Result { Ok(Vote { actor: ObjectId::new(actor.actor_id()), @@ -84,7 +85,7 @@ impl Vote { object: ObjectId::new(object.ap_id()), cc: [ObjectId::new(community.actor_id())], kind: kind.clone(), - id: generate_activity_id(kind)?, + id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?, context: lemmy_context(), unparsed: Default::default(), }) @@ -101,7 +102,7 @@ impl Vote { Community::read(conn, community_id) }) .await??; - let vote = Vote::new(object, actor, &community, kind)?; + let vote = Vote::new(object, actor, &community, kind, context)?; let vote_id = vote.id.clone(); let activity = AnnouncableActivities::Vote(vote); @@ -116,7 +117,7 @@ impl ActivityHandler for Vote { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self)?; + verify_activity(self, &context.settings())?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; Ok(()) } diff --git a/crates/apub/src/activity_queue.rs b/crates/apub/src/activity_queue.rs index 01959c7d7..f05c4fce4 100644 --- a/crates/apub/src/activity_queue.rs +++ b/crates/apub/src/activity_queue.rs @@ -16,7 +16,7 @@ use background_jobs::{ WorkerConfig, }; use lemmy_db_schema::source::community::Community; -use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; +use lemmy_utils::{location_info, LemmyError}; use lemmy_websocket::LemmyContext; use log::{info, warn}; use reqwest::Client; @@ -56,7 +56,7 @@ pub(crate) async fn send_activity_new( where T: Serialize, { - if !Settings::get().federation.enabled || inboxes.is_empty() { + if !context.settings().federation.enabled || inboxes.is_empty() { return Ok(()); } @@ -64,7 +64,7 @@ where // Don't send anything to ourselves // TODO: this should be a debug assert - let hostname = Settings::get().get_hostname_without_port()?; + let hostname = context.settings().get_hostname_without_port()?; let inboxes: Vec<&Url> = inboxes .iter() .filter(|i| i.domain().expect("valid inbox url") != hostname) diff --git a/crates/apub/src/fetcher/community.rs b/crates/apub/src/fetcher/community.rs index 99dd3f85e..174741128 100644 --- a/crates/apub/src/fetcher/community.rs +++ b/crates/apub/src/fetcher/community.rs @@ -73,8 +73,13 @@ pub(crate) async fn fetch_community_outbox( outbox: &Url, recursion_counter: &mut i32, ) -> Result<(), LemmyError> { - let outbox = - fetch_remote_object::(context.client(), outbox, recursion_counter).await?; + let outbox = fetch_remote_object::( + context.client(), + &context.settings(), + outbox, + recursion_counter, + ) + .await?; let outbox_activities = outbox.items().context(location_info!())?.clone(); let mut outbox_activities = outbox_activities.many().context(location_info!())?; if outbox_activities.len() > 20 { @@ -98,9 +103,13 @@ async fn fetch_community_mods( recursion_counter: &mut i32, ) -> Result, LemmyError> { if let Some(mods_url) = &group.moderators { - let mods = - fetch_remote_object::(context.client(), mods_url, recursion_counter) - .await?; + let mods = fetch_remote_object::( + context.client(), + &context.settings(), + mods_url, + recursion_counter, + ) + .await?; let mods = mods .items() .map(|i| i.as_many()) diff --git a/crates/apub/src/fetcher/fetch.rs b/crates/apub/src/fetcher/fetch.rs index 1e6f8de89..29c7f9df6 100644 --- a/crates/apub/src/fetcher/fetch.rs +++ b/crates/apub/src/fetcher/fetch.rs @@ -1,6 +1,6 @@ use crate::{check_is_apub_id_valid, APUB_JSON_CONTENT_TYPE}; use anyhow::anyhow; -use lemmy_utils::{request::retry, LemmyError}; +use lemmy_utils::{request::retry, settings::structs::Settings, LemmyError}; use log::info; use reqwest::Client; use serde::Deserialize; @@ -18,6 +18,7 @@ static MAX_REQUEST_NUMBER: i32 = 25; /// timeouts etc. pub(in crate::fetcher) async fn fetch_remote_object( client: &Client, + settings: &Settings, url: &Url, recursion_counter: &mut i32, ) -> Result @@ -28,7 +29,7 @@ where if *recursion_counter > MAX_REQUEST_NUMBER { return Err(anyhow!("Maximum recursion depth reached").into()); } - check_is_apub_id_valid(url, false)?; + check_is_apub_id_valid(url, false, settings)?; let timeout = Duration::from_secs(60); diff --git a/crates/apub/src/fetcher/objects.rs b/crates/apub/src/fetcher/objects.rs new file mode 100644 index 000000000..c222776d5 --- /dev/null +++ b/crates/apub/src/fetcher/objects.rs @@ -0,0 +1,107 @@ +use crate::{ + fetcher::fetch::fetch_remote_object, + objects::{comment::Note, post::Page, FromApub}, + PostOrComment, +}; +use anyhow::anyhow; +use diesel::result::Error::NotFound; +use lemmy_api_common::blocking; +use lemmy_db_queries::{ApubObject, Crud}; +use lemmy_db_schema::source::{comment::Comment, post::Post}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use log::debug; +use url::Url; + +/// Gets a post by its apub ID. If it exists locally, it is returned directly. Otherwise it is +/// pulled from its apub ID, inserted and returned. +/// +/// The parent community is also pulled if necessary. Comments are not pulled. +pub(crate) async fn get_or_fetch_and_insert_post( + post_ap_id: &Url, + context: &LemmyContext, + recursion_counter: &mut i32, +) -> Result { + let post_ap_id_owned = post_ap_id.to_owned(); + let post = blocking(context.pool(), move |conn| { + Post::read_from_apub_id(conn, &post_ap_id_owned.into()) + }) + .await?; + + match post { + Ok(p) => Ok(p), + Err(NotFound {}) => { + debug!("Fetching and creating remote post: {}", post_ap_id); + let page = fetch_remote_object::( + context.client(), + &context.settings(), + post_ap_id, + recursion_counter, + ) + .await?; + let post = Post::from_apub(&page, context, post_ap_id, recursion_counter).await?; + + Ok(post) + } + Err(e) => Err(e.into()), + } +} + +/// Gets a comment by its apub ID. If it exists locally, it is returned directly. Otherwise it is +/// pulled from its apub ID, inserted and returned. +/// +/// The parent community, post and comment are also pulled if necessary. +pub(crate) async fn get_or_fetch_and_insert_comment( + comment_ap_id: &Url, + context: &LemmyContext, + recursion_counter: &mut i32, +) -> Result { + let comment_ap_id_owned = comment_ap_id.to_owned(); + let comment = blocking(context.pool(), move |conn| { + Comment::read_from_apub_id(conn, &comment_ap_id_owned.into()) + }) + .await?; + + match comment { + Ok(p) => Ok(p), + Err(NotFound {}) => { + debug!( + "Fetching and creating remote comment and its parents: {}", + comment_ap_id + ); + let comment = fetch_remote_object::( + context.client(), + &context.settings(), + comment_ap_id, + recursion_counter, + ) + .await?; + let comment = Comment::from_apub(&comment, context, comment_ap_id, recursion_counter).await?; + + let post_id = comment.post_id; + let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; + if post.locked { + return Err(anyhow!("Post is locked").into()); + } + + Ok(comment) + } + Err(e) => Err(e.into()), + } +} + +pub(crate) async fn get_or_fetch_and_insert_post_or_comment( + ap_id: &Url, + context: &LemmyContext, + recursion_counter: &mut i32, +) -> Result { + Ok( + match get_or_fetch_and_insert_post(ap_id, context, recursion_counter).await { + Ok(p) => PostOrComment::Post(Box::new(p)), + Err(_) => { + let c = get_or_fetch_and_insert_comment(ap_id, context, recursion_counter).await?; + PostOrComment::Comment(Box::new(c)) + } + }, + ) +} diff --git a/crates/apub/src/fetcher/person.rs b/crates/apub/src/fetcher/person.rs new file mode 100644 index 000000000..54c3163a6 --- /dev/null +++ b/crates/apub/src/fetcher/person.rs @@ -0,0 +1,80 @@ +use crate::{ + fetcher::{fetch::fetch_remote_object, is_deleted, should_refetch_actor}, + objects::{person::Person as ApubPerson, FromApub}, +}; +use anyhow::anyhow; +use diesel::result::Error::NotFound; +use lemmy_api_common::blocking; +use lemmy_db_queries::{source::person::Person_, ApubObject}; +use lemmy_db_schema::source::person::Person; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use log::debug; +use url::Url; + +/// Get a person from its apub ID. +/// +/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database. +/// Otherwise it is fetched from the remote instance, stored and returned. +pub(crate) async fn get_or_fetch_and_upsert_person( + apub_id: &Url, + context: &LemmyContext, + recursion_counter: &mut i32, +) -> Result { + let apub_id_owned = apub_id.to_owned(); + let person = blocking(context.pool(), move |conn| { + Person::read_from_apub_id(conn, &apub_id_owned.into()) + }) + .await?; + + match person { + // If its older than a day, re-fetch it + Ok(u) if !u.local && should_refetch_actor(u.last_refreshed_at) => { + debug!("Fetching and updating from remote person: {}", apub_id); + let person = fetch_remote_object::( + context.client(), + &context.settings(), + apub_id, + recursion_counter, + ) + .await; + + if is_deleted(&person) { + // TODO: use Person::update_deleted() once implemented + blocking(context.pool(), move |conn| { + Person::delete_account(conn, u.id) + }) + .await??; + return Err(anyhow!("Person was deleted by remote instance").into()); + } else if person.is_err() { + return Ok(u); + } + + let person = Person::from_apub(&person?, context, apub_id, recursion_counter).await?; + + let person_id = person.id; + blocking(context.pool(), move |conn| { + Person::mark_as_updated(conn, person_id) + }) + .await??; + + Ok(person) + } + Ok(u) => Ok(u), + Err(NotFound {}) => { + debug!("Fetching and creating remote person: {}", apub_id); + let person = fetch_remote_object::( + context.client(), + &context.settings(), + apub_id, + recursion_counter, + ) + .await?; + + let person = Person::from_apub(&person, context, apub_id, recursion_counter).await?; + + Ok(person) + } + Err(e) => Err(e.into()), + } +} diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs index 6a3cc14f0..da152f2f9 100644 --- a/crates/apub/src/fetcher/search.rs +++ b/crates/apub/src/fetcher/search.rs @@ -45,7 +45,14 @@ pub async fn search_by_apub_id( // remote actor, use webfinger to resolve url if name.contains('@') { let (name, domain) = name.splitn(2, '@').collect_tuple().expect("invalid query"); - webfinger_resolve_actor(name, domain, kind, context.client()).await? + webfinger_resolve_actor( + name, + domain, + kind, + context.client(), + context.settings().get_protocol_string(), + ) + .await? } // local actor, read from database and return else { diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index 47182259f..a70ed6f00 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -23,7 +23,7 @@ use lemmy_api_common::blocking; use lemmy_apub_lib::{ActivityFields, ActivityHandler}; use lemmy_db_queries::{source::activity::Activity_, DbPool}; use lemmy_db_schema::source::activity::Activity; -use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; +use lemmy_utils::{location_info, LemmyError}; use lemmy_websocket::LemmyContext; use log::{info, trace}; use serde::{Deserialize, Serialize}; @@ -98,10 +98,10 @@ where if is_activity_already_known(context.pool(), activity.id_unchecked()).await? { return Ok(HttpResponse::Ok().finish()); } - check_is_apub_id_valid(activity.actor(), false)?; + check_is_apub_id_valid(activity.actor(), false, &context.settings())?; info!("Verifying activity {}", activity.id_unchecked().to_string()); activity.verify(context, request_counter).await?; - assert_activity_not_local(&activity)?; + assert_activity_not_local(&activity, &context.settings().hostname)?; // Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen // if we receive the same activity twice in very quick succession. @@ -151,7 +151,7 @@ pub(crate) async fn get_activity( info: web::Path, context: web::Data, ) -> Result, LemmyError> { - let settings = Settings::get(); + let settings = context.settings(); let activity_id = Url::parse(&format!( "{}/activities/{}/{}", settings.get_protocol_and_hostname(), @@ -187,10 +187,13 @@ pub(crate) async fn is_activity_already_known( } } -fn assert_activity_not_local(activity: &T) -> Result<(), LemmyError> { +fn assert_activity_not_local( + activity: &T, + hostname: &str, +) -> Result<(), LemmyError> { let activity_domain = activity.id_unchecked().domain().context(location_info!())?; - if activity_domain == Settings::get().hostname { + if activity_domain == hostname { return Err( anyhow!( "Error: received activity which was sent by local instance: {:?}", diff --git a/crates/apub/src/http/routes.rs b/crates/apub/src/http/routes.rs index cd6b11486..612cc4586 100644 --- a/crates/apub/src/http/routes.rs +++ b/crates/apub/src/http/routes.rs @@ -24,9 +24,9 @@ use sha2::{Digest, Sha256}; static APUB_JSON_CONTENT_TYPE_LONG: &str = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; -pub fn config(cfg: &mut web::ServiceConfig) { - if Settings::get().federation.enabled { - println!("federation enabled, host is {}", Settings::get().hostname); +pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) { + if settings.federation.enabled { + println!("federation enabled, host is {}", settings.hostname); let digest_verifier = VerifyDigest::new(Sha256::new()); let header_guard_accept = guard::Any(guard::Header("Accept", APUB_JSON_CONTENT_TYPE)) diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index e7065752a..027da7f7a 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -37,8 +37,8 @@ static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json"; pub(crate) fn check_is_apub_id_valid( apub_id: &Url, use_strict_allowlist: bool, + settings: &Settings, ) -> Result<(), LemmyError> { - let settings = Settings::get(); let domain = apub_id.domain().context(location_info!())?.to_string(); let local_instance = settings.get_hostname_without_port()?; @@ -62,22 +62,22 @@ pub(crate) fn check_is_apub_id_valid( return Err(anyhow!("invalid hostname {}: {}", host, apub_id).into()); } - if apub_id.scheme() != Settings::get().get_protocol_string() { + if apub_id.scheme() != settings.get_protocol_string() { return Err(anyhow!("invalid apub id scheme {}: {}", apub_id.scheme(), apub_id).into()); } // TODO: might be good to put the part above in one method, and below in another // (which only gets called in apub::objects) // -> no that doesnt make sense, we still need the code below for blocklist and strict allowlist - if let Some(blocked) = Settings::get().federation.blocked_instances { + if let Some(blocked) = settings.to_owned().federation.blocked_instances { if blocked.contains(&domain) { return Err(anyhow!("{} is in federation blocklist", domain).into()); } } - if let Some(mut allowed) = Settings::get().federation.allowed_instances { + if let Some(mut allowed) = settings.to_owned().federation.allowed_instances { // Only check allowlist if this is a community, or strict allowlist is enabled. - let strict_allowlist = Settings::get().federation.strict_allowlist; + let strict_allowlist = settings.to_owned().federation.strict_allowlist; if use_strict_allowlist || strict_allowlist { // need to allow this explicitly because apub receive might contain objects from our local // instance. @@ -128,7 +128,11 @@ trait ActorType { #[async_trait::async_trait(?Send)] pub trait CommunityType { fn followers_url(&self) -> Url; - async fn get_follower_inboxes(&self, pool: &DbPool) -> Result, LemmyError>; + async fn get_follower_inboxes( + &self, + pool: &DbPool, + settings: &Settings, + ) -> Result, LemmyError>; } pub enum EndpointType { @@ -160,12 +164,9 @@ fn generate_apub_endpoint_for_domain( pub fn generate_apub_endpoint( endpoint_type: EndpointType, name: &str, + protocol_and_hostname: &str, ) -> Result { - generate_apub_endpoint_for_domain( - endpoint_type, - name, - &Settings::get().get_protocol_and_hostname(), - ) + generate_apub_endpoint_for_domain(endpoint_type, name, protocol_and_hostname) } pub fn generate_followers_url(actor_id: &DbUrl) -> Result { @@ -200,6 +201,7 @@ fn generate_moderators_url(community_id: &DbUrl) -> Result { pub fn build_actor_id_from_shortname( endpoint_type: EndpointType, short_name: &str, + settings: &Settings, ) -> Result { let split = short_name.split('@').collect::>(); @@ -207,9 +209,9 @@ pub fn build_actor_id_from_shortname( // If there's no @, its local let domain = if split.len() == 1 { - Settings::get().get_protocol_and_hostname() + settings.get_protocol_and_hostname() } else { - format!("{}://{}", Settings::get().get_protocol_string(), split[1]) + format!("{}://{}", settings.get_protocol_string(), split[1]) }; generate_apub_endpoint_for_domain(endpoint_type, name, &domain) diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index f33448710..e27817d6f 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -222,7 +222,7 @@ impl FromApub for Comment { } let content = ¬e.source.content; - let content_slurs_removed = remove_slurs(content); + let content_slurs_removed = remove_slurs(content, &context.settings().slur_regex()); let form = CommentForm { creator_id: creator.id, diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index dcd00e6d5..c8ddd2972 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -24,6 +24,7 @@ use lemmy_db_schema::{ source::community::{Community, CommunityForm}, }; use lemmy_utils::{ + settings::structs::Settings, utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html}, LemmyError, }; @@ -74,6 +75,7 @@ impl Group { pub(crate) async fn from_apub_to_form( group: &Group, expected_domain: &Url, + settings: &Settings, ) -> Result { let actor_id = Some(group.id(expected_domain)?.clone().into()); let name = group.preferred_username.clone(); @@ -81,9 +83,10 @@ impl Group { let description = group.source.clone().map(|s| s.content); let shared_inbox = group.endpoints.shared_inbox.clone().map(|s| s.into()); - check_slurs(&name)?; - check_slurs(&title)?; - check_slurs_opt(&description)?; + let slur_regex = &settings.slur_regex(); + check_slurs(&name, slur_regex)?; + check_slurs(&title, slur_regex)?; + check_slurs_opt(&description, slur_regex)?; Ok(CommunityForm { name, @@ -175,7 +178,7 @@ impl FromApub for Community { expected_domain: &Url, request_counter: &mut i32, ) -> Result { - let form = Group::from_apub_to_form(group, expected_domain).await?; + let form = Group::from_apub_to_form(group, expected_domain, &context.settings()).await?; let community = blocking(context.pool(), move |conn| Community::upsert(conn, &form)).await??; update_community_mods(group, &community, context, request_counter).await?; diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index 04af848ff..5bf7a5897 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -150,10 +150,12 @@ impl FromApub for DbPerson { UserTypes::Service => true, }; - check_slurs(&name)?; - check_slurs_opt(&display_name)?; - check_slurs_opt(&bio)?; - check_is_apub_id_valid(&person.id, false)?; + let slur_regex = &context.settings().slur_regex(); + check_slurs(&name, slur_regex)?; + check_slurs_opt(&display_name, slur_regex)?; + check_slurs_opt(&bio, slur_regex)?; + + check_is_apub_id_valid(&person.id, false, &context.settings())?; let person_form = PersonForm { name, diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 341b428b6..451ce0554 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -100,7 +100,7 @@ impl Page { ) -> Result<(), LemmyError> { let community = extract_community(&self.to, context, request_counter).await?; - check_slurs(&self.name)?; + check_slurs(&self.name, &context.settings().slur_regex())?; verify_domains_match(self.attributed_to.inner(), &self.id)?; verify_person_in_community( &self.attributed_to, @@ -191,7 +191,7 @@ impl FromApub for Post { let thumbnail_url: Option = page.image.clone().map(|i| i.url); let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url { - fetch_site_data(context.client(), Some(url)).await + fetch_site_data(context.client(), &context.settings(), Some(url)).await } else { (None, thumbnail_url) }; @@ -199,7 +199,10 @@ impl FromApub for Post { .map(|u| (u.title, u.description, u.html)) .unwrap_or((None, None, None)); - let body_slurs_removed = page.source.as_ref().map(|s| remove_slurs(&s.content)); + let body_slurs_removed = page + .source + .as_ref() + .map(|s| remove_slurs(&s.content, &context.settings().slur_regex())); let form = PostForm { name: page.name.clone(), url: page.url.clone().map(|u| u.into()), diff --git a/crates/apub_lib/src/webfinger.rs b/crates/apub_lib/src/webfinger.rs index ebd49ea67..a5395d099 100644 --- a/crates/apub_lib/src/webfinger.rs +++ b/crates/apub_lib/src/webfinger.rs @@ -1,7 +1,6 @@ use anyhow::anyhow; use lemmy_utils::{ request::{retry, RecvError}, - settings::structs::Settings, LemmyError, }; use log::debug; @@ -38,6 +37,7 @@ pub async fn webfinger_resolve_actor( domain: &str, webfinger_type: WebfingerType, client: &Client, + protocol_string: &str, ) -> Result { let webfinger_type = match webfinger_type { WebfingerType::Person => "acct", @@ -45,11 +45,7 @@ pub async fn webfinger_resolve_actor( }; let fetch_url = format!( "{}://{}/.well-known/webfinger?resource={}:{}@{}", - Settings::get().get_protocol_string(), - domain, - webfinger_type, - name, - domain + protocol_string, domain, webfinger_type, name, domain ); debug!("Fetching webfinger url: {}", &fetch_url); diff --git a/crates/db_queries/src/source/secret.rs b/crates/db_queries/src/source/secret.rs index eac0d7eb4..6181549fe 100644 --- a/crates/db_queries/src/source/secret.rs +++ b/crates/db_queries/src/source/secret.rs @@ -1,36 +1,18 @@ use diesel::{result::Error, *}; use lemmy_db_schema::source::secret::Secret; -use lemmy_utils::settings::structs::Settings; -use std::sync::RwLock; -use crate::get_database_url_from_env; - -lazy_static! { - static ref SECRET: RwLock = RwLock::new(init().expect("Failed to load secrets from DB.")); -} - -pub trait SecretSingleton { - fn get() -> Secret; +pub trait Secret_ { + fn init(conn: &PgConnection) -> Result; } -impl SecretSingleton for Secret { - /// Returns the Secret as a struct - fn get() -> Self { - SECRET.read().expect("read secrets").to_owned() +impl Secret_ for Secret { + /// Initialize the Secrets from the DB. + /// Warning: You should only call this once. + fn init(conn: &PgConnection) -> Result { + read_secrets(conn) } } -/// Reads the secrets from the DB -fn init() -> Result { - let db_url = match get_database_url_from_env() { - Ok(url) => url, - Err(_) => Settings::get().get_database_url(), - }; - - let conn = PgConnection::establish(&db_url).expect("Couldn't get DB connection for Secrets."); - read_secrets(&conn) -} - fn read_secrets(conn: &PgConnection) -> Result { use lemmy_db_schema::schema::secret::dsl::*; secret.first::(conn) diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index 5cf2f1759..66e23ee3e 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -4,13 +4,13 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use diesel::PgConnection; use lemmy_api_common::blocking; use lemmy_db_queries::{ - source::{community::Community_, person::Person_, secret::SecretSingleton}, + source::{community::Community_, person::Person_}, Crud, ListingType, SortType, }; use lemmy_db_schema::{ - source::{community::Community, local_user::LocalUser, person::Person, secret::Secret}, + source::{community::Community, local_user::LocalUser, person::Person}, LocalUserId, }; use lemmy_db_views::{ @@ -19,12 +19,7 @@ use lemmy_db_views::{ site_view::SiteView, }; use lemmy_db_views_actor::person_mention_view::{PersonMentionQueryBuilder, PersonMentionView}; -use lemmy_utils::{ - claims::Claims, - settings::structs::Settings, - utils::markdown_to_html, - LemmyError, -}; +use lemmy_utils::{claims::Claims, utils::markdown_to_html, LemmyError}; use lemmy_websocket::LemmyContext; use rss::{ extension::dublincore::DublinCoreExtensionBuilder, @@ -98,7 +93,7 @@ async fn get_feed_data( }) .await??; - let items = create_post_items(posts)?; + let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?; let mut channel_builder = ChannelBuilder::default(); channel_builder @@ -108,7 +103,7 @@ async fn get_feed_data( site_view.site.name, listing_type.to_string() )) - .link(Settings::get().get_protocol_and_hostname()) + .link(context.settings().get_protocol_and_hostname()) .items(items); if let Some(site_desc) = site_view.site.description { @@ -141,11 +136,20 @@ async fn get_feed( _ => return Err(ErrorBadRequest(LemmyError::from(anyhow!("wrong_type")))), }; + let jwt_secret = context.secret().jwt_secret.to_owned(); + let protocol_and_hostname = context.settings().get_protocol_and_hostname(); + let builder = blocking(context.pool(), move |conn| match request_type { - RequestType::User => get_feed_user(conn, &sort_type, param), - RequestType::Community => get_feed_community(conn, &sort_type, param), - RequestType::Front => get_feed_front(conn, &sort_type, param), - RequestType::Inbox => get_feed_inbox(conn, param), + RequestType::User => get_feed_user(conn, &sort_type, ¶m, &protocol_and_hostname), + RequestType::Community => get_feed_community(conn, &sort_type, ¶m, &protocol_and_hostname), + RequestType::Front => get_feed_front( + conn, + &jwt_secret, + &sort_type, + ¶m, + &protocol_and_hostname, + ), + RequestType::Inbox => get_feed_inbox(conn, &jwt_secret, ¶m, &protocol_and_hostname), }) .await? .map_err(ErrorBadRequest)?; @@ -170,10 +174,11 @@ fn get_sort_type(info: web::Query) -> Result { fn get_feed_user( conn: &PgConnection, sort_type: &SortType, - user_name: String, + user_name: &str, + protocol_and_hostname: &str, ) -> Result { let site_view = SiteView::read(conn)?; - let person = Person::find_by_name(conn, &user_name)?; + let person = Person::find_by_name(conn, user_name)?; let posts = PostQueryBuilder::create(conn) .listing_type(ListingType::All) @@ -181,7 +186,7 @@ fn get_feed_user( .creator_id(person.id) .list()?; - let items = create_post_items(posts)?; + let items = create_post_items(posts, protocol_and_hostname)?; let mut channel_builder = ChannelBuilder::default(); channel_builder @@ -196,10 +201,11 @@ fn get_feed_user( fn get_feed_community( conn: &PgConnection, sort_type: &SortType, - community_name: String, + community_name: &str, + protocol_and_hostname: &str, ) -> Result { let site_view = SiteView::read(conn)?; - let community = Community::read_from_name(conn, &community_name)?; + let community = Community::read_from_name(conn, community_name)?; let posts = PostQueryBuilder::create(conn) .listing_type(ListingType::All) @@ -207,7 +213,7 @@ fn get_feed_community( .community_id(community.id) .list()?; - let items = create_post_items(posts)?; + let items = create_post_items(posts, protocol_and_hostname)?; let mut channel_builder = ChannelBuilder::default(); channel_builder @@ -225,12 +231,13 @@ fn get_feed_community( fn get_feed_front( conn: &PgConnection, + jwt_secret: &str, sort_type: &SortType, - jwt: String, + jwt: &str, + protocol_and_hostname: &str, ) -> Result { let site_view = SiteView::read(conn)?; - let jwt_secret = Secret::get().jwt_secret; - let local_user_id = LocalUserId(Claims::decode(&jwt, &jwt_secret)?.claims.sub); + let local_user_id = LocalUserId(Claims::decode(jwt, jwt_secret)?.claims.sub); let local_user = LocalUser::read(conn, local_user_id)?; let posts = PostQueryBuilder::create(conn) @@ -241,13 +248,13 @@ fn get_feed_front( .sort(*sort_type) .list()?; - let items = create_post_items(posts)?; + let items = create_post_items(posts, protocol_and_hostname)?; let mut channel_builder = ChannelBuilder::default(); channel_builder .namespaces(RSS_NAMESPACE.to_owned()) .title(&format!("{} - Subscribed", site_view.site.name)) - .link(Settings::get().get_protocol_and_hostname()) + .link(protocol_and_hostname) .items(items); if let Some(site_desc) = site_view.site.description { @@ -257,10 +264,14 @@ fn get_feed_front( Ok(channel_builder) } -fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result { +fn get_feed_inbox( + conn: &PgConnection, + jwt_secret: &str, + jwt: &str, + protocol_and_hostname: &str, +) -> Result { let site_view = SiteView::read(conn)?; - let jwt_secret = Secret::get().jwt_secret; - let local_user_id = LocalUserId(Claims::decode(&jwt, &jwt_secret)?.claims.sub); + let local_user_id = LocalUserId(Claims::decode(jwt, jwt_secret)?.claims.sub); let local_user = LocalUser::read(conn, local_user_id)?; let person_id = local_user.person_id; let show_bot_accounts = local_user.show_bot_accounts; @@ -280,16 +291,13 @@ fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result Result, mentions: Vec, + protocol_and_hostname: &str, ) -> Result, LemmyError> { let mut reply_items: Vec = replies .iter() .map(|r| { let reply_url = format!( "{}/post/{}/comment/{}", - Settings::get().get_protocol_and_hostname(), - r.post.id, - r.comment.id + protocol_and_hostname, r.post.id, r.comment.id ); build_item( &r.creator.name, &r.comment.published, &reply_url, &r.comment.content, + protocol_and_hostname, ) }) .collect::, LemmyError>>()?; @@ -326,15 +334,14 @@ fn create_reply_and_mention_items( .map(|m| { let mention_url = format!( "{}/post/{}/comment/{}", - Settings::get().get_protocol_and_hostname(), - m.post.id, - m.comment.id + protocol_and_hostname, m.post.id, m.comment.id ); build_item( &m.creator.name, &m.comment.published, &mention_url, &m.comment.content, + protocol_and_hostname, ) }) .collect::, LemmyError>>()?; @@ -348,14 +355,11 @@ fn build_item( published: &NaiveDateTime, url: &str, content: &str, + protocol_and_hostname: &str, ) -> Result { let mut i = ItemBuilder::default(); i.title(format!("Reply from {}", creator_name)); - let author_url = format!( - "{}/u/{}", - Settings::get().get_protocol_and_hostname(), - creator_name - ); + let author_url = format!("{}/u/{}", protocol_and_hostname, creator_name); i.author(format!( "/u/{} (link)", creator_name, author_url @@ -376,7 +380,10 @@ fn build_item( Ok(i.build().map_err(|e| anyhow!(e))?) } -fn create_post_items(posts: Vec) -> Result, LemmyError> { +fn create_post_items( + posts: Vec, + protocol_and_hostname: &str, +) -> Result, LemmyError> { let mut items: Vec = Vec::new(); for p in posts { @@ -390,11 +397,7 @@ fn create_post_items(posts: Vec) -> Result, LemmyError> { let dt = DateTime::::from_utc(p.post.published, Utc); i.pub_date(dt.to_rfc2822()); - let post_url = format!( - "{}/post/{}", - Settings::get().get_protocol_and_hostname(), - p.post.id - ); + let post_url = format!("{}/post/{}", protocol_and_hostname, p.post.id); i.link(post_url.to_owned()); i.comments(post_url.to_owned()); let guid = GuidBuilder::default() @@ -404,11 +407,7 @@ fn create_post_items(posts: Vec) -> Result, LemmyError> { .map_err(|e| anyhow!(e))?; i.guid(guid); - let community_url = format!( - "{}/c/{}", - Settings::get().get_protocol_and_hostname(), - p.community.name - ); + let community_url = format!("{}/c/{}", protocol_and_hostname, p.community.name); // TODO add images let mut description = format!("submitted by {} to {}
{} points | {} comments", diff --git a/crates/routes/src/images.rs b/crates/routes/src/images.rs index 230841d44..e5e33489d 100644 --- a/crates/routes/src/images.rs +++ b/crates/routes/src/images.rs @@ -2,9 +2,8 @@ use actix_http::http::header::ACCEPT_ENCODING; use actix_web::{body::BodyStream, http::StatusCode, web::Data, *}; use anyhow::anyhow; use awc::Client; -use lemmy_db_queries::source::secret::SecretSingleton; -use lemmy_db_schema::source::secret::Secret; -use lemmy_utils::{claims::Claims, rate_limit::RateLimit, settings::structs::Settings, LemmyError}; +use lemmy_utils::{claims::Claims, rate_limit::RateLimit, LemmyError}; +use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -48,18 +47,21 @@ async fn upload( req: HttpRequest, body: web::Payload, client: web::Data, + context: web::Data, ) -> Result { // TODO: check rate limit here let jwt = req .cookie("jwt") .expect("No auth header for picture upload"); - let jwt_secret = Secret::get().jwt_secret; - if Claims::decode(jwt.value(), &jwt_secret).is_err() { + if Claims::decode(jwt.value(), &context.secret().jwt_secret).is_err() { return Ok(HttpResponse::Unauthorized().finish()); }; - let mut client_req = client.request_from(format!("{}/image", pictrs_url()?), req.head()); + let mut client_req = client.request_from( + format!("{}/image", pictrs_url(context.settings().pictrs_url)?), + req.head(), + ); // remove content-encoding header so that pictrs doesnt send gzipped response client_req.headers_mut().remove(ACCEPT_ENCODING); @@ -82,17 +84,28 @@ async fn full_res( web::Query(params): web::Query, req: HttpRequest, client: web::Data, + context: web::Data, ) -> Result { let name = &filename.into_inner(); // If there are no query params, the URL is original + let pictrs_url_settings = context.settings().pictrs_url; let url = if params.format.is_none() && params.thumbnail.is_none() { - format!("{}/image/original/{}", pictrs_url()?, name,) + format!( + "{}/image/original/{}", + pictrs_url(pictrs_url_settings)?, + name, + ) } else { // Use jpg as a default when none is given let format = params.format.unwrap_or_else(|| "jpg".to_string()); - let mut url = format!("{}/image/process.{}?src={}", pictrs_url()?, format, name,); + let mut url = format!( + "{}/image/process.{}?src={}", + pictrs_url(pictrs_url_settings)?, + format, + name, + ); if let Some(size) = params.thumbnail { url = format!("{}&thumbnail={}", url, size,); @@ -138,10 +151,16 @@ async fn delete( components: web::Path<(String, String)>, req: HttpRequest, client: web::Data, + context: web::Data, ) -> Result { let (token, file) = components.into_inner(); - let url = format!("{}/image/delete/{}/{}", pictrs_url()?, &token, &file); + let url = format!( + "{}/image/delete/{}/{}", + pictrs_url(context.settings().pictrs_url)?, + &token, + &file + ); let mut client_req = client.request_from(url, req.head()); client_req.headers_mut().remove(ACCEPT_ENCODING); @@ -159,8 +178,6 @@ async fn delete( Ok(HttpResponse::build(res.status()).body(BodyStream::new(res))) } -fn pictrs_url() -> Result { - Settings::get() - .pictrs_url - .ok_or_else(|| anyhow!("images_disabled").into()) +fn pictrs_url(pictrs_url: Option) -> Result { + pictrs_url.ok_or_else(|| anyhow!("images_disabled").into()) } diff --git a/crates/routes/src/nodeinfo.rs b/crates/routes/src/nodeinfo.rs index 86d625900..9e327efae 100644 --- a/crates/routes/src/nodeinfo.rs +++ b/crates/routes/src/nodeinfo.rs @@ -2,7 +2,7 @@ use actix_web::{body::Body, error::ErrorBadRequest, *}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_db_views::site_view::SiteView; -use lemmy_utils::{settings::structs::Settings, version, LemmyError}; +use lemmy_utils::{version, LemmyError}; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; use url::Url; @@ -13,13 +13,15 @@ pub fn config(cfg: &mut web::ServiceConfig) { .route("/.well-known/nodeinfo", web::get().to(node_info_well_known)); } -async fn node_info_well_known() -> Result, LemmyError> { +async fn node_info_well_known( + context: web::Data, +) -> Result, LemmyError> { let node_info = NodeInfoWellKnown { links: NodeInfoWellKnownLinks { rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?, href: Url::parse(&format!( "{}/nodeinfo/2.0.json", - Settings::get().get_protocol_and_hostname() + &context.settings().get_protocol_and_hostname(), ))?, }, }; @@ -31,7 +33,7 @@ async fn node_info(context: web::Data) -> Result, context: web::Data, ) -> Result { - let community_regex_parsed = WEBFINGER_COMMUNITY_REGEX + let community_regex_parsed = context + .settings() + .webfinger_community_regex() .captures(&info.resource) .map(|c| c.get(1)) .flatten(); - let username_regex_parsed = WEBFINGER_USERNAME_REGEX + let username_regex_parsed = context + .settings() + .webfinger_username_regex() .captures(&info.resource) .map(|c| c.get(1)) .flatten(); diff --git a/crates/utils/src/claims.rs b/crates/utils/src/claims.rs index 2b6ce9854..d68b4119d 100644 --- a/crates/utils/src/claims.rs +++ b/crates/utils/src/claims.rs @@ -1,4 +1,4 @@ -use crate::{settings::structs::Settings, LemmyError}; +use crate::LemmyError; use chrono::Utc; use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation}; use serde::{Deserialize, Serialize}; @@ -24,10 +24,10 @@ impl Claims { Ok(decode::(jwt, &key, &v)?) } - pub fn jwt(local_user_id: i32, jwt_secret: &str) -> Result { + pub fn jwt(local_user_id: i32, jwt_secret: &str, hostname: &str) -> Result { let my_claims = Claims { sub: local_user_id, - iss: Settings::get().hostname, + iss: hostname.to_string(), iat: Utc::now().timestamp(), }; diff --git a/crates/utils/src/email.rs b/crates/utils/src/email.rs index 3e5307421..77f83d020 100644 --- a/crates/utils/src/email.rs +++ b/crates/utils/src/email.rs @@ -18,9 +18,10 @@ pub fn send_email( to_email: &str, to_username: &str, html: &str, + settings: &Settings, ) -> Result<(), String> { - let email_config = Settings::get().email.ok_or("no_email_setup")?; - let domain = Settings::get().hostname; + let email_config = settings.email.to_owned().ok_or("no_email_setup")?; + let domain = settings.hostname.to_owned(); let (smtp_server, smtp_port) = { let email_and_port = email_config.smtp_server.split(':').collect::>(); diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index f1093fd58..ad539f8b0 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -17,9 +17,8 @@ mod test; pub mod utils; pub mod version; -use crate::settings::structs::Settings; use http::StatusCode; -use regex::Regex; + use std::fmt; use thiserror::Error; @@ -88,16 +87,3 @@ impl actix_web::error::ResponseError for LemmyError { } } } - -lazy_static! { - pub static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!( - "^group:([a-z0-9_]{{3,}})@{}$", - Settings::get().hostname - )) - .expect("compile webfinger regex"); - pub static ref WEBFINGER_USERNAME_REGEX: Regex = Regex::new(&format!( - "^acct:([a-z0-9_]{{3,}})@{}$", - Settings::get().hostname - )) - .expect("compile webfinger regex"); -} diff --git a/crates/utils/src/rate_limit/mod.rs b/crates/utils/src/rate_limit/mod.rs index ca7f124cd..c1a4627c1 100644 --- a/crates/utils/src/rate_limit/mod.rs +++ b/crates/utils/src/rate_limit/mod.rs @@ -1,9 +1,4 @@ -use crate::{ - settings::structs::{RateLimitConfig, Settings}, - utils::get_ip, - IpAddr, - LemmyError, -}; +use crate::{settings::structs::RateLimitConfig, utils::get_ip, IpAddr, LemmyError}; use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; use futures::future::{ok, Ready}; use rate_limiter::{RateLimitType, RateLimiter}; @@ -22,11 +17,13 @@ pub struct RateLimit { // it might be reasonable to use a std::sync::Mutex here, since we don't need to lock this // across await points pub rate_limiter: Arc>, + pub rate_limit_config: RateLimitConfig, } #[derive(Debug, Clone)] pub struct RateLimited { rate_limiter: Arc>, + rate_limit_config: RateLimitConfig, type_: RateLimitType, } @@ -55,6 +52,7 @@ impl RateLimit { fn kind(&self, type_: RateLimitType) -> RateLimited { RateLimited { rate_limiter: self.rate_limiter.clone(), + rate_limit_config: self.rate_limit_config.clone(), type_, } } @@ -71,7 +69,7 @@ impl RateLimited { { // Does not need to be blocking because the RwLock in settings never held across await points, // and the operation here locks only long enough to clone - let rate_limit: RateLimitConfig = Settings::get().rate_limit.unwrap_or_default(); + let rate_limit = self.rate_limit_config; // before { diff --git a/crates/utils/src/request.rs b/crates/utils/src/request.rs index f1655710e..f143b1781 100644 --- a/crates/utils/src/request.rs +++ b/crates/utils/src/request.rs @@ -120,9 +120,10 @@ pub(crate) struct PictrsFile { pub(crate) async fn fetch_pictrs( client: &Client, + settings: &Settings, image_url: &Url, ) -> Result { - if let Some(pictrs_url) = Settings::get().pictrs_url { + if let Some(pictrs_url) = settings.pictrs_url.to_owned() { is_image_content_type(client, image_url).await?; let fetch_url = format!( @@ -152,6 +153,7 @@ pub(crate) async fn fetch_pictrs( /// Returns the SiteMetadata, and a Pictrs URL, if there is a picture associated pub async fn fetch_site_data( client: &Client, + settings: &Settings, url: Option<&Url>, ) -> (Option, Option) { match &url { @@ -166,16 +168,16 @@ pub async fn fetch_site_data( Some(metadata_res) => match &metadata_res.image { // Metadata, with image // Try to generate a small thumbnail if there's a full sized one from post-links - Some(metadata_image) => fetch_pictrs(client, metadata_image) + Some(metadata_image) => fetch_pictrs(client, settings, metadata_image) .await .map(|r| r.files[0].file.to_owned()), // Metadata, but no image - None => fetch_pictrs(client, url) + None => fetch_pictrs(client, settings, url) .await .map(|r| r.files[0].file.to_owned()), }, // No metadata, try to fetch the URL as an image - None => fetch_pictrs(client, url) + None => fetch_pictrs(client, settings, url) .await .map(|r| r.files[0].file.to_owned()), }; @@ -185,7 +187,7 @@ pub async fn fetch_site_data( .map(|p| { Url::parse(&format!( "{}/pictrs/image/{}", - Settings::get().get_protocol_and_hostname(), + settings.get_protocol_and_hostname(), p )) .ok() diff --git a/crates/utils/src/settings/mod.rs b/crates/utils/src/settings/mod.rs index d4776326f..d47cc5b4b 100644 --- a/crates/utils/src/settings/mod.rs +++ b/crates/utils/src/settings/mod.rs @@ -1,6 +1,7 @@ use crate::{location_info, settings::structs::Settings, LemmyError}; use anyhow::{anyhow, Context}; use deser_hjson::from_str; +use regex::{Regex, RegexBuilder}; use std::{env, fs, io::Error, sync::RwLock}; pub mod structs; @@ -17,14 +18,25 @@ impl Settings { /// /// Note: The env var `LEMMY_DATABASE_URL` is parsed in /// `lemmy_db_queries/src/lib.rs::get_database_url_from_env()` - fn init() -> Result { + /// Warning: Only call this once. + pub fn init() -> Result { // Read the config file - let config = from_str::(&Self::read_config_file()?)?; + let mut config = from_str::(&Self::read_config_file()?)?; if config.hostname == "unset" { return Err(anyhow!("Hostname variable is not set!").into()); } + // Initialize the regexes + config.webfinger_community_regex = Some( + Regex::new(&format!("^group:([a-z0-9_]{{3,}})@{}$", config.hostname)) + .expect("compile webfinger regex"), + ); + config.webfinger_username_regex = Some( + Regex::new(&format!("^acct:([a-z0-9_]{{3,}})@{}$", config.hostname)) + .expect("compile webfinger regex"), + ); + Ok(config) } @@ -92,4 +104,30 @@ impl Settings { Ok(Self::read_config_file()?) } + + pub fn webfinger_community_regex(&self) -> Regex { + self + .webfinger_community_regex + .to_owned() + .expect("compile webfinger regex") + } + + pub fn webfinger_username_regex(&self) -> Regex { + self + .webfinger_username_regex + .to_owned() + .expect("compile webfinger regex") + } + + pub fn slur_regex(&self) -> Regex { + let mut slurs = r"(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)".to_string(); + if let Some(additional_slurs) = &self.additional_slurs { + slurs.push('|'); + slurs.push_str(additional_slurs); + }; + RegexBuilder::new(&slurs) + .case_insensitive(true) + .build() + .expect("compile regex") + } } diff --git a/crates/utils/src/settings/structs.rs b/crates/utils/src/settings/structs.rs index 9ba8a5f94..293349348 100644 --- a/crates/utils/src/settings/structs.rs +++ b/crates/utils/src/settings/structs.rs @@ -1,3 +1,4 @@ +use regex::Regex; use serde::Deserialize; use std::net::{IpAddr, Ipv4Addr}; @@ -30,6 +31,12 @@ pub struct Settings { pub additional_slurs: Option, #[default(20)] pub actor_name_max_length: usize, + #[default(None)] + #[serde(skip)] + pub webfinger_community_regex: Option, + #[default(None)] + #[serde(skip)] + pub webfinger_username_regex: Option, } #[derive(Debug, Deserialize, Clone, SmartDefault)] diff --git a/crates/utils/src/test.rs b/crates/utils/src/test.rs index 33bf38700..a59d04129 100644 --- a/crates/utils/src/test.rs +++ b/crates/utils/src/test.rs @@ -1,12 +1,15 @@ -use crate::utils::{ - is_valid_actor_name, - is_valid_display_name, - is_valid_matrix_id, - is_valid_post_title, - remove_slurs, - scrape_text_for_mentions, - slur_check, - slurs_vec_to_str, +use crate::{ + settings::structs::Settings, + utils::{ + is_valid_actor_name, + is_valid_display_name, + is_valid_matrix_id, + is_valid_post_title, + remove_slurs, + scrape_text_for_mentions, + slur_check, + slurs_vec_to_str, + }, }; #[test] @@ -21,23 +24,28 @@ fn test_mentions_regex() { #[test] fn test_valid_actor_name() { - assert!(is_valid_actor_name("Hello_98")); - assert!(is_valid_actor_name("ten")); - assert!(!is_valid_actor_name("Hello-98")); - assert!(!is_valid_actor_name("a")); - assert!(!is_valid_actor_name("")); + let actor_name_max_length = Settings::init().unwrap().actor_name_max_length; + assert!(is_valid_actor_name("Hello_98", actor_name_max_length)); + assert!(is_valid_actor_name("ten", actor_name_max_length)); + assert!(!is_valid_actor_name("Hello-98", actor_name_max_length)); + assert!(!is_valid_actor_name("a", actor_name_max_length)); + assert!(!is_valid_actor_name("", actor_name_max_length)); } #[test] fn test_valid_display_name() { - assert!(is_valid_display_name("hello @there")); - assert!(!is_valid_display_name("@hello there")); + let actor_name_max_length = Settings::init().unwrap().actor_name_max_length; + assert!(is_valid_display_name("hello @there", actor_name_max_length)); + assert!(!is_valid_display_name( + "@hello there", + actor_name_max_length + )); // Make sure zero-space with an @ doesn't work - assert!(!is_valid_display_name(&format!( - "{}@my name is", - '\u{200b}' - ))); + assert!(!is_valid_display_name( + &format!("{}@my name is", '\u{200b}'), + actor_name_max_length + )); } #[test] @@ -57,11 +65,12 @@ fn test_valid_matrix_id() { #[test] fn test_slur_filter() { + let slur_regex = Settings::init().unwrap().slur_regex(); let test = "faggot test kike tranny cocksucker retardeds. Capitalized Niggerz. This is a bunch of other safe text."; let slur_free = "No slurs here"; assert_eq!( - remove_slurs(test), + remove_slurs(test, &slur_regex), "*removed* test *removed* *removed* *removed* *removed*. Capitalized *removed*. This is a bunch of other safe text." .to_string() ); @@ -76,9 +85,9 @@ fn test_slur_filter() { ]; let has_slurs_err_str = "No slurs - Niggerz, cocksucker, faggot, kike, retardeds, tranny"; - assert_eq!(slur_check(test), Err(has_slurs_vec)); - assert_eq!(slur_check(slur_free), Ok(())); - if let Err(slur_vec) = slur_check(test) { + assert_eq!(slur_check(test, &slur_regex), Err(has_slurs_vec)); + assert_eq!(slur_check(slur_free, &slur_regex), Ok(())); + if let Err(slur_vec) = slur_check(test, &slur_regex) { assert_eq!(&slurs_vec_to_str(slur_vec), has_slurs_err_str); } } diff --git a/crates/utils/src/utils.rs b/crates/utils/src/utils.rs index 0fb1f7888..97384fc2f 100644 --- a/crates/utils/src/utils.rs +++ b/crates/utils/src/utils.rs @@ -1,22 +1,13 @@ -use crate::{settings::structs::Settings, ApiError, IpAddr}; +use crate::{ApiError, IpAddr}; use actix_web::dev::ConnectionInfo; use chrono::{DateTime, FixedOffset, NaiveDateTime}; use itertools::Itertools; use rand::{distributions::Alphanumeric, thread_rng, Rng}; -use regex::{Regex, RegexBuilder}; +use regex::Regex; use url::Url; lazy_static! { static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").expect("compile regex"); - static ref SLUR_REGEX: Regex = { - let mut slurs = r"(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)".to_string(); - if let Some(additional_slurs) = Settings::get().additional_slurs { - slurs.push('|'); - slurs.push_str(&additional_slurs); - }; - RegexBuilder::new(&slurs).case_insensitive(true).build().expect("compile regex") - }; - static ref USERNAME_MATCHES_REGEX: Regex = Regex::new(r"/u/[a-zA-Z][0-9a-zA-Z_]*").expect("compile regex"); // TODO keep this old one, it didn't work with port well tho @@ -37,12 +28,12 @@ pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime { DateTime::::from_utc(datetime, FixedOffset::east(0)) } -pub fn remove_slurs(test: &str) -> String { - SLUR_REGEX.replace_all(test, "*removed*").to_string() +pub fn remove_slurs(test: &str, slur_regex: &Regex) -> String { + slur_regex.replace_all(test, "*removed*").to_string() } -pub(crate) fn slur_check(test: &str) -> Result<(), Vec<&str>> { - let mut matches: Vec<&str> = SLUR_REGEX.find_iter(test).map(|mat| mat.as_str()).collect(); +pub(crate) fn slur_check<'a>(test: &'a str, slur_regex: &'a Regex) -> Result<(), Vec<&'a str>> { + let mut matches: Vec<&str> = slur_regex.find_iter(test).map(|mat| mat.as_str()).collect(); // Unique matches.sort_unstable(); @@ -55,17 +46,17 @@ pub(crate) fn slur_check(test: &str) -> Result<(), Vec<&str>> { } } -pub fn check_slurs(text: &str) -> Result<(), ApiError> { - if let Err(slurs) = slur_check(text) { +pub fn check_slurs(text: &str, slur_regex: &Regex) -> Result<(), ApiError> { + if let Err(slurs) = slur_check(text, slur_regex) { Err(ApiError::err(&slurs_vec_to_str(slurs))) } else { Ok(()) } } -pub fn check_slurs_opt(text: &Option) -> Result<(), ApiError> { +pub fn check_slurs_opt(text: &Option, slur_regex: &Regex) -> Result<(), ApiError> { match text { - Some(t) => check_slurs(t), + Some(t) => check_slurs(t, slur_regex), None => Ok(()), } } @@ -96,8 +87,8 @@ pub struct MentionData { } impl MentionData { - pub fn is_local(&self) -> bool { - Settings::get().hostname.eq(&self.domain) + pub fn is_local(&self, hostname: &str) -> bool { + hostname.eq(&self.domain) } pub fn full_name(&self) -> String { format!("@{}@{}", &self.name, &self.domain) @@ -115,17 +106,16 @@ pub fn scrape_text_for_mentions(text: &str) -> Vec { out.into_iter().unique().collect() } -pub fn is_valid_actor_name(name: &str) -> bool { - name.chars().count() <= Settings::get().actor_name_max_length - && VALID_ACTOR_NAME_REGEX.is_match(name) +pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> bool { + name.chars().count() <= actor_name_max_length && VALID_ACTOR_NAME_REGEX.is_match(name) } // Can't do a regex here, reverse lookarounds not supported -pub fn is_valid_display_name(name: &str) -> bool { +pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> bool { !name.starts_with('@') && !name.starts_with('\u{200b}') && name.chars().count() >= 3 - && name.chars().count() <= Settings::get().actor_name_max_length + && name.chars().count() <= actor_name_max_length } pub fn is_valid_matrix_id(matrix_id: &str) -> bool { diff --git a/crates/websocket/src/chat_server.rs b/crates/websocket/src/chat_server.rs index 2e4fda2e4..7ab65aad2 100644 --- a/crates/websocket/src/chat_server.rs +++ b/crates/websocket/src/chat_server.rs @@ -14,10 +14,11 @@ use diesel::{ PgConnection, }; use lemmy_api_common::{comment::*, post::*}; -use lemmy_db_schema::{CommunityId, LocalUserId, PostId}; +use lemmy_db_schema::{source::secret::Secret, CommunityId, LocalUserId, PostId}; use lemmy_utils::{ location_info, rate_limit::RateLimit, + settings::structs::Settings, ApiError, ConnectionId, IpAddr, @@ -71,6 +72,12 @@ pub struct ChatServer { /// The DB Pool pub(super) pool: Pool>, + /// The Settings + pub(super) settings: Settings, + + /// The Secrets + pub(super) secret: Secret, + /// Rate limiting based on rate type and IP addr pub(super) rate_limiter: RateLimit, @@ -95,6 +102,7 @@ pub struct SessionInfo { /// And manages available rooms. Peers send messages to other peers in same /// room through `ChatServer`. impl ChatServer { + #![allow(clippy::too_many_arguments)] pub fn startup( pool: Pool>, rate_limiter: RateLimit, @@ -102,6 +110,8 @@ impl ChatServer { message_handler_crud: MessageHandlerCrudType, client: Client, activity_queue: QueueHandle, + settings: Settings, + secret: Secret, ) -> ChatServer { ChatServer { sessions: HashMap::new(), @@ -117,6 +127,8 @@ impl ChatServer { message_handler_crud, client, activity_queue, + settings, + secret, } } @@ -452,6 +464,8 @@ impl ChatServer { chat_server: ctx.address(), client: self.client.to_owned(), activity_queue: self.activity_queue.to_owned(), + settings: self.settings.to_owned(), + secret: self.secret.to_owned(), }; let message_handler_crud = self.message_handler_crud; let message_handler = self.message_handler; diff --git a/crates/websocket/src/lib.rs b/crates/websocket/src/lib.rs index d501e9611..366512eb9 100644 --- a/crates/websocket/src/lib.rs +++ b/crates/websocket/src/lib.rs @@ -5,7 +5,8 @@ use crate::chat_server::ChatServer; use actix::Addr; use background_jobs::QueueHandle; use lemmy_db_queries::DbPool; -use lemmy_utils::LemmyError; +use lemmy_db_schema::source::secret::Secret; +use lemmy_utils::{settings::structs::Settings, LemmyError}; use reqwest::Client; use serde::Serialize; @@ -20,6 +21,8 @@ pub struct LemmyContext { pub chat_server: Addr, pub client: Client, pub activity_queue: QueueHandle, + pub settings: Settings, + pub secret: Secret, } impl LemmyContext { @@ -28,12 +31,16 @@ impl LemmyContext { chat_server: Addr, client: Client, activity_queue: QueueHandle, + settings: Settings, + secret: Secret, ) -> LemmyContext { LemmyContext { pool, chat_server, client, activity_queue, + settings, + secret, } } pub fn pool(&self) -> &DbPool { @@ -48,6 +55,13 @@ impl LemmyContext { pub fn activity_queue(&self) -> &QueueHandle { &self.activity_queue } + pub fn settings(&self) -> Settings { + // TODO hacky solution to be able to hotload the settings. + Settings::get() + } + pub fn secret(&self) -> &Secret { + &self.secret + } } impl Clone for LemmyContext { @@ -57,6 +71,8 @@ impl Clone for LemmyContext { chat_server: self.chat_server.clone(), client: self.client.clone(), activity_queue: self.activity_queue.clone(), + settings: self.settings.clone(), + secret: self.secret.clone(), } } } diff --git a/src/code_migrations.rs b/src/code_migrations.rs index cebf12936..1f9381986 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -24,22 +24,28 @@ use lemmy_db_schema::{ private_message::PrivateMessage, }, }; -use lemmy_utils::{apub::generate_actor_keypair, settings::structs::Settings, LemmyError}; +use lemmy_utils::{apub::generate_actor_keypair, LemmyError}; use log::info; -pub fn run_advanced_migrations(conn: &PgConnection) -> Result<(), LemmyError> { - user_updates_2020_04_02(conn)?; - community_updates_2020_04_02(conn)?; - post_updates_2020_04_03(conn)?; - comment_updates_2020_04_03(conn)?; - private_message_updates_2020_05_05(conn)?; - post_thumbnail_url_updates_2020_07_27(conn)?; +pub fn run_advanced_migrations( + conn: &PgConnection, + protocol_and_hostname: &str, +) -> Result<(), LemmyError> { + user_updates_2020_04_02(conn, protocol_and_hostname)?; + community_updates_2020_04_02(conn, protocol_and_hostname)?; + post_updates_2020_04_03(conn, protocol_and_hostname)?; + comment_updates_2020_04_03(conn, protocol_and_hostname)?; + private_message_updates_2020_05_05(conn, protocol_and_hostname)?; + post_thumbnail_url_updates_2020_07_27(conn, protocol_and_hostname)?; apub_columns_2021_02_02(conn)?; Ok(()) } -fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> { +fn user_updates_2020_04_02( + conn: &PgConnection, + protocol_and_hostname: &str, +) -> Result<(), LemmyError> { use lemmy_db_schema::schema::person::dsl::*; info!("Running user_updates_2020_04_02"); @@ -55,7 +61,11 @@ fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> { let form = PersonForm { name: cperson.name.to_owned(), - actor_id: Some(generate_apub_endpoint(EndpointType::Person, &cperson.name)?), + actor_id: Some(generate_apub_endpoint( + EndpointType::Person, + &cperson.name, + protocol_and_hostname, + )?), private_key: Some(Some(keypair.private_key)), public_key: Some(Some(keypair.public_key)), last_refreshed_at: Some(naive_now()), @@ -70,7 +80,10 @@ fn user_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> { Ok(()) } -fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> { +fn community_updates_2020_04_02( + conn: &PgConnection, + protocol_and_hostname: &str, +) -> Result<(), LemmyError> { use lemmy_db_schema::schema::community::dsl::*; info!("Running community_updates_2020_04_02"); @@ -83,7 +96,11 @@ fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> { for ccommunity in &incorrect_communities { let keypair = generate_actor_keypair()?; - let community_actor_id = generate_apub_endpoint(EndpointType::Community, &ccommunity.name)?; + let community_actor_id = generate_apub_endpoint( + EndpointType::Community, + &ccommunity.name, + protocol_and_hostname, + )?; let form = CommunityForm { name: ccommunity.name.to_owned(), @@ -114,7 +131,10 @@ fn community_updates_2020_04_02(conn: &PgConnection) -> Result<(), LemmyError> { Ok(()) } -fn post_updates_2020_04_03(conn: &PgConnection) -> Result<(), LemmyError> { +fn post_updates_2020_04_03( + conn: &PgConnection, + protocol_and_hostname: &str, +) -> Result<(), LemmyError> { use lemmy_db_schema::schema::post::dsl::*; info!("Running post_updates_2020_04_03"); @@ -126,7 +146,11 @@ fn post_updates_2020_04_03(conn: &PgConnection) -> Result<(), LemmyError> { .load::(conn)?; for cpost in &incorrect_posts { - let apub_id = generate_apub_endpoint(EndpointType::Post, &cpost.id.to_string())?; + let apub_id = generate_apub_endpoint( + EndpointType::Post, + &cpost.id.to_string(), + protocol_and_hostname, + )?; Post::update_ap_id(conn, cpost.id, apub_id)?; } @@ -135,7 +159,10 @@ fn post_updates_2020_04_03(conn: &PgConnection) -> Result<(), LemmyError> { Ok(()) } -fn comment_updates_2020_04_03(conn: &PgConnection) -> Result<(), LemmyError> { +fn comment_updates_2020_04_03( + conn: &PgConnection, + protocol_and_hostname: &str, +) -> Result<(), LemmyError> { use lemmy_db_schema::schema::comment::dsl::*; info!("Running comment_updates_2020_04_03"); @@ -147,7 +174,11 @@ fn comment_updates_2020_04_03(conn: &PgConnection) -> Result<(), LemmyError> { .load::(conn)?; for ccomment in &incorrect_comments { - let apub_id = generate_apub_endpoint(EndpointType::Comment, &ccomment.id.to_string())?; + let apub_id = generate_apub_endpoint( + EndpointType::Comment, + &ccomment.id.to_string(), + protocol_and_hostname, + )?; Comment::update_ap_id(conn, ccomment.id, apub_id)?; } @@ -156,7 +187,10 @@ fn comment_updates_2020_04_03(conn: &PgConnection) -> Result<(), LemmyError> { Ok(()) } -fn private_message_updates_2020_05_05(conn: &PgConnection) -> Result<(), LemmyError> { +fn private_message_updates_2020_05_05( + conn: &PgConnection, + protocol_and_hostname: &str, +) -> Result<(), LemmyError> { use lemmy_db_schema::schema::private_message::dsl::*; info!("Running private_message_updates_2020_05_05"); @@ -168,7 +202,11 @@ fn private_message_updates_2020_05_05(conn: &PgConnection) -> Result<(), LemmyEr .load::(conn)?; for cpm in &incorrect_pms { - let apub_id = generate_apub_endpoint(EndpointType::PrivateMessage, &cpm.id.to_string())?; + let apub_id = generate_apub_endpoint( + EndpointType::PrivateMessage, + &cpm.id.to_string(), + protocol_and_hostname, + )?; PrivateMessage::update_ap_id(conn, cpm.id, apub_id)?; } @@ -177,15 +215,15 @@ fn private_message_updates_2020_05_05(conn: &PgConnection) -> Result<(), LemmyEr Ok(()) } -fn post_thumbnail_url_updates_2020_07_27(conn: &PgConnection) -> Result<(), LemmyError> { +fn post_thumbnail_url_updates_2020_07_27( + conn: &PgConnection, + protocol_and_hostname: &str, +) -> Result<(), LemmyError> { use lemmy_db_schema::schema::post::dsl::*; info!("Running post_thumbnail_url_updates_2020_07_27"); - let domain_prefix = format!( - "{}/pictrs/image/", - Settings::get().get_protocol_and_hostname(), - ); + let domain_prefix = format!("{}/pictrs/image/", protocol_and_hostname,); let incorrect_thumbnails = post.filter(thumbnail_url.not_like("http%")); diff --git a/src/main.rs b/src/main.rs index 504ccc80e..4e827d72a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,8 @@ use lemmy_api::match_websocket_operation; use lemmy_api_common::blocking; use lemmy_api_crud::match_websocket_operation_crud; use lemmy_apub::activity_queue::create_activity_queue; -use lemmy_db_queries::get_database_url_from_env; +use lemmy_db_queries::{get_database_url_from_env, source::secret::Secret_}; +use lemmy_db_schema::source::secret::Secret; use lemmy_routes::{feeds, images, nodeinfo, webfinger}; use lemmy_server::{api_routes, code_migrations::run_advanced_migrations, scheduled_tasks}; use lemmy_utils::{ @@ -29,7 +30,7 @@ embed_migrations!(); #[actix_web::main] async fn main() -> Result<(), LemmyError> { env_logger::init(); - let settings = Settings::get(); + let settings = Settings::init().expect("Couldn't initialize settings."); // Set up the r2d2 connection pool let db_url = match get_database_url_from_env() { @@ -43,9 +44,10 @@ async fn main() -> Result<(), LemmyError> { .unwrap_or_else(|_| panic!("Error connecting to {}", db_url)); // Run the migrations from code + let protocol_and_hostname = settings.get_protocol_and_hostname(); blocking(&pool, move |conn| { embedded_migrations::run(conn)?; - run_advanced_migrations(conn)?; + run_advanced_migrations(conn, &protocol_and_hostname)?; Ok(()) as Result<(), LemmyError> }) .await??; @@ -58,8 +60,13 @@ async fn main() -> Result<(), LemmyError> { // Set up the rate limiter let rate_limiter = RateLimit { rate_limiter: Arc::new(Mutex::new(RateLimiter::default())), + rate_limit_config: settings.rate_limit.to_owned().unwrap_or_default(), }; + // Initialize the secrets + let conn = pool.get()?; + let secret = Secret::init(&conn).expect("Couldn't initialize secrets."); + println!( "Starting http server at {}:{}", settings.bind, settings.port @@ -74,16 +81,21 @@ async fn main() -> Result<(), LemmyError> { |c, i, o, d| Box::pin(match_websocket_operation_crud(c, i, o, d)), Client::default(), activity_queue.clone(), + settings.clone(), + secret.clone(), ) .start(); // Create Http server with websocket support + let settings_bind = settings.clone(); HttpServer::new(move || { let context = LemmyContext::create( pool.clone(), chat_server.to_owned(), Client::default(), activity_queue.to_owned(), + settings.to_owned(), + secret.to_owned(), ); let rate_limiter = rate_limiter.clone(); App::new() @@ -91,13 +103,13 @@ async fn main() -> Result<(), LemmyError> { .app_data(Data::new(context)) // The routes .configure(|cfg| api_routes::config(cfg, &rate_limiter)) - .configure(lemmy_apub::http::routes::config) + .configure(|cfg| lemmy_apub::http::routes::config(cfg, &settings)) .configure(feeds::config) .configure(|cfg| images::config(cfg, &rate_limiter)) .configure(nodeinfo::config) - .configure(webfinger::config) + .configure(|cfg| webfinger::config(cfg, &settings)) }) - .bind((settings.bind, settings.port))? + .bind((settings_bind.bind, settings_bind.port))? .run() .await?;