From d28e5245d2ae1e496993ef902e8dde7eb8c6ef70 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 12 Aug 2020 07:13:44 -0400 Subject: [PATCH 1/2] Don't allow preferred usernames to start with @. Fixes #1058 (#1076) * Don't allow preferred usernames to start with @. Fixes #1058 * Trim the preferred username. --- server/lemmy_utils/src/lib.rs | 12 ++++++++++++ server/src/api/user.rs | 8 +++++++- ui/src/components/user-details.tsx | 2 +- ui/src/components/user.tsx | 3 ++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/server/lemmy_utils/src/lib.rs b/server/lemmy_utils/src/lib.rs index fc50e199b..1a89beaa7 100644 --- a/server/lemmy_utils/src/lib.rs +++ b/server/lemmy_utils/src/lib.rs @@ -162,6 +162,11 @@ pub fn is_valid_username(name: &str) -> bool { VALID_USERNAME_REGEX.is_match(name) } +// Can't do a regex here, reverse lookarounds not supported +pub fn is_valid_preferred_username(preferred_username: &str) -> bool { + !preferred_username.starts_with("@") && preferred_username.len() >=3 && preferred_username.len() <= 20 +} + pub fn is_valid_community_name(name: &str) -> bool { VALID_COMMUNITY_NAME_REGEX.is_match(name) } @@ -176,6 +181,7 @@ mod tests { is_valid_community_name, is_valid_post_title, is_valid_username, + is_valid_preferred_username, remove_slurs, scrape_text_for_mentions, slur_check, @@ -201,6 +207,12 @@ mod tests { assert!(!is_valid_username("")); } + #[test] + fn test_valid_preferred_username() { + assert!(is_valid_preferred_username("hello @there")); + assert!(!is_valid_preferred_username("@hello there")); + } + #[test] fn test_valid_community_name() { assert!(is_valid_community_name("example")); diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 2d5895170..83d8470cd 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -51,6 +51,7 @@ use lemmy_db::{ use lemmy_utils::{ generate_actor_keypair, generate_random_string, + is_valid_preferred_username, is_valid_username, make_apub_endpoint, naive_from_unix, @@ -576,7 +577,12 @@ impl Perform for Oper { // The DB constraint should stop too many characters let preferred_username = match &data.preferred_username { - Some(preferred_username) => Some(preferred_username.to_owned()), + Some(preferred_username) => { + if !is_valid_preferred_username(preferred_username.trim()) { + return Err(APIError::err("invalid_username").into()); + } + Some(preferred_username.trim().to_string()) + } None => read_user.preferred_username, }; diff --git a/ui/src/components/user-details.tsx b/ui/src/components/user-details.tsx index 5e9a58d22..b3ce294f3 100644 --- a/ui/src/components/user-details.tsx +++ b/ui/src/components/user-details.tsx @@ -79,6 +79,7 @@ export class UserDetails extends Component { componentDidMount() { this.fetchUserData(); + setupTippy(); } componentDidUpdate(lastProps: UserDetailsProps) { @@ -88,7 +89,6 @@ export class UserDetails extends Component { break; } } - setupTippy(); } fetchUserData() { diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index d7db0ae2a..13cc90aca 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -180,6 +180,7 @@ export class User extends Component { ); WebSocketService.Instance.getSite(); + setupTippy(); } get isCurrentUser() { @@ -226,7 +227,6 @@ export class User extends Component { // Couldnt get a refresh working. This does for now. location.reload(); } - setupTippy(); } get documentTitle(): string { @@ -565,6 +565,7 @@ export class User extends Component { this, this.handleUserSettingsPreferredUsernameChange )} + pattern="^(?!@)(.+)$" minLength={3} maxLength={20} /> From 8daeba450c504dd1c7d5a5f4c8707d88ec4e17a7 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 12 Aug 2020 07:31:45 -0400 Subject: [PATCH 2/2] Removing unnecessary oper struct. Fixes #660 (#1077) --- server/src/api/comment.rs | 70 ++++++++++----------- server/src/api/community.rs | 74 ++++++++++++---------- server/src/api/mod.rs | 12 +--- server/src/api/post.rs | 84 +++++++++++++------------ server/src/api/site.rs | 57 +++++++++-------- server/src/api/user.rs | 108 +++++++++++++++++++-------------- server/src/routes/api.rs | 16 ++--- server/src/websocket/server.rs | 6 +- 8 files changed, 227 insertions(+), 200 deletions(-) diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index e3189d434..cb366e3eb 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -5,7 +5,6 @@ use crate::{ get_user_from_jwt_opt, is_mod_or_admin, APIError, - Oper, Perform, }, apub::{ApubLikeableType, ApubObjectType}, @@ -18,6 +17,7 @@ use crate::{ DbPool, LemmyError, }; +use actix_web::client::Client; use lemmy_db::{ comment::*, comment_view::*, @@ -121,15 +121,16 @@ pub struct GetCommentsResponse { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for CreateComment { type Response = CommentResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &CreateComment = &self.data; + let data: &CreateComment = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let content_slurs_removed = remove_slurs(&data.content.to_owned()); @@ -180,9 +181,7 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err("couldnt_create_comment").into()), }; - updated_comment - .send_create(&user, &self.client, pool) - .await?; + updated_comment.send_create(&user, &client, pool).await?; // Scan the comment for user mentions, add those rows let mentions = scrape_text_for_mentions(&comment_form.content); @@ -202,7 +201,7 @@ impl Perform for Oper { return Err(APIError::err("couldnt_like_comment").into()); } - updated_comment.send_like(&user, &self.client, pool).await?; + updated_comment.send_like(&user, &client, pool).await?; let user_id = user.id; let comment_view = blocking(pool, move |conn| { @@ -233,15 +232,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for EditComment { type Response = CommentResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &EditComment = &self.data; + let data: &EditComment = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let edit_id = data.edit_id; @@ -268,9 +268,7 @@ impl Perform for Oper { }; // Send the apub update - updated_comment - .send_update(&user, &self.client, pool) - .await?; + updated_comment.send_update(&user, &client, pool).await?; // Do the mentions / recipients let post_id = orig_comment.post_id; @@ -311,15 +309,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for DeleteComment { type Response = CommentResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &DeleteComment = &self.data; + let data: &DeleteComment = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let edit_id = data.edit_id; @@ -346,12 +345,10 @@ impl Perform for Oper { // Send the apub message if deleted { - updated_comment - .send_delete(&user, &self.client, pool) - .await?; + updated_comment.send_delete(&user, &client, pool).await?; } else { updated_comment - .send_undo_delete(&user, &self.client, pool) + .send_undo_delete(&user, &client, pool) .await?; } @@ -393,15 +390,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for RemoveComment { type Response = CommentResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &RemoveComment = &self.data; + let data: &RemoveComment = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let edit_id = data.edit_id; @@ -435,12 +433,10 @@ impl Perform for Oper { // Send the apub message if removed { - updated_comment - .send_remove(&user, &self.client, pool) - .await?; + updated_comment.send_remove(&user, &client, pool).await?; } else { updated_comment - .send_undo_remove(&user, &self.client, pool) + .send_undo_remove(&user, &client, pool) .await?; } @@ -482,15 +478,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for MarkCommentAsRead { type Response = CommentResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &MarkCommentAsRead = &self.data; + let data: &MarkCommentAsRead = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let edit_id = data.edit_id; @@ -545,15 +542,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for SaveComment { type Response = CommentResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &SaveComment = &self.data; + let data: &SaveComment = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let comment_saved_form = CommentSavedForm { @@ -589,15 +587,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for CreateCommentLike { type Response = CommentResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &CreateCommentLike = &self.data; + let data: &CreateCommentLike = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let mut recipient_ids = Vec::new(); @@ -659,12 +658,12 @@ impl Perform for Oper { } if like_form.score == 1 { - comment.send_like(&user, &self.client, pool).await?; + comment.send_like(&user, &client, pool).await?; } else if like_form.score == -1 { - comment.send_dislike(&user, &self.client, pool).await?; + comment.send_dislike(&user, &client, pool).await?; } } else { - comment.send_undo_like(&user, &self.client, pool).await?; + comment.send_undo_like(&user, &client, pool).await?; } // Have to refetch the comment to get the current state @@ -698,15 +697,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetComments { type Response = GetCommentsResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetComments = &self.data; + let data: &GetComments = &self; let user = get_user_from_jwt_opt(&data.auth, pool).await?; let user_id = user.map(|u| u.id); diff --git a/server/src/api/community.rs b/server/src/api/community.rs index fb153895f..3aebb9ee9 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -1,6 +1,6 @@ use super::*; use crate::{ - api::{is_admin, is_mod_or_admin, APIError, Oper, Perform}, + api::{is_admin, is_mod_or_admin, APIError, Perform}, apub::ActorType, blocking, websocket::{ @@ -10,6 +10,7 @@ use crate::{ }, DbPool, }; +use actix_web::client::Client; use lemmy_db::{ diesel_option_overwrite, naive_now, @@ -155,15 +156,16 @@ pub struct TransferCommunity { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetCommunity { type Response = GetCommunityResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetCommunity = &self.data; + let data: &GetCommunity = &self; let user = get_user_from_jwt_opt(&data.auth, pool).await?; let user_id = user.map(|u| u.id); @@ -226,15 +228,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for CreateCommunity { type Response = CommunityResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &CreateCommunity = &self.data; + let data: &CreateCommunity = &self; let user = get_user_from_jwt(&data.auth, pool).await?; check_slurs(&data.name)?; @@ -318,15 +321,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for EditCommunity { type Response = CommunityResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &EditCommunity = &self.data; + let data: &EditCommunity = &self; let user = get_user_from_jwt(&data.auth, pool).await?; check_slurs(&data.title)?; @@ -400,15 +404,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for DeleteCommunity { type Response = CommunityResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &DeleteCommunity = &self.data; + let data: &DeleteCommunity = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Verify its the creator (only a creator can delete the community) @@ -432,12 +437,10 @@ impl Perform for Oper { // Send apub messages if deleted { - updated_community - .send_delete(&user, &self.client, pool) - .await?; + updated_community.send_delete(&user, &client, pool).await?; } else { updated_community - .send_undo_delete(&user, &self.client, pool) + .send_undo_delete(&user, &client, pool) .await?; } @@ -459,15 +462,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for RemoveCommunity { type Response = CommunityResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &RemoveCommunity = &self.data; + let data: &RemoveCommunity = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Verify its an admin (only an admin can remove a community) @@ -501,12 +505,10 @@ impl Perform for Oper { // Apub messages if removed { - updated_community - .send_remove(&user, &self.client, pool) - .await?; + updated_community.send_remove(&user, &client, pool).await?; } else { updated_community - .send_undo_remove(&user, &self.client, pool) + .send_undo_remove(&user, &client, pool) .await?; } @@ -528,15 +530,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for ListCommunities { type Response = ListCommunitiesResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &ListCommunities = &self.data; + let data: &ListCommunities = &self; let user = get_user_from_jwt_opt(&data.auth, pool).await?; let user_id = match &user { @@ -570,15 +573,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for FollowCommunity { type Response = CommunityResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + client: Client, ) -> Result { - let data: &FollowCommunity = &self.data; + let data: &FollowCommunity = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let community_id = data.community_id; @@ -605,11 +609,11 @@ impl Perform for Oper { // Dont actually add to the community followers here, because you need // to wait for the accept user - .send_follow(&community.actor_id()?, &self.client, pool) + .send_follow(&community.actor_id()?, &client, pool) .await?; } else { user - .send_unfollow(&community.actor_id()?, &self.client, pool) + .send_unfollow(&community.actor_id()?, &client, pool) .await?; let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form); if blocking(pool, unfollow).await?.is_err() { @@ -632,15 +636,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetFollowedCommunities { type Response = GetFollowedCommunitiesResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetFollowedCommunities = &self.data; + let data: &GetFollowedCommunities = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let user_id = user.id; @@ -659,15 +664,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for BanFromCommunity { type Response = BanFromCommunityResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &BanFromCommunity = &self.data; + let data: &BanFromCommunity = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let community_id = data.community_id; @@ -731,15 +737,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for AddModToCommunity { type Response = AddModToCommunityResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &AddModToCommunity = &self.data; + let data: &AddModToCommunity = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let community_moderator_form = CommunityModeratorForm { @@ -795,15 +802,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for TransferCommunity { type Response = GetCommunityResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &TransferCommunity = &self.data; + let data: &TransferCommunity = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let community_id = data.community_id; diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 8124cd4a1..01884d384 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -33,17 +33,6 @@ impl APIError { } } -pub struct Oper { - data: T, - client: Client, -} - -impl Oper { - pub fn new(data: Data, client: Client) -> Oper { - Oper { data, client } - } -} - #[async_trait::async_trait(?Send)] pub trait Perform { type Response: serde::ser::Serialize + Send; @@ -52,6 +41,7 @@ pub trait Perform { &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result; } diff --git a/server/src/api/post.rs b/server/src/api/post.rs index b43e4e55c..4d089bff7 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -7,7 +7,6 @@ use crate::{ get_user_from_jwt_opt, is_mod_or_admin, APIError, - Oper, Perform, }, apub::{ApubLikeableType, ApubObjectType}, @@ -21,6 +20,7 @@ use crate::{ DbPool, LemmyError, }; +use actix_web::client::Client; use lemmy_db::{ comment_view::*, community_view::*, @@ -140,15 +140,16 @@ pub struct SavePost { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for CreatePost { type Response = PostResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &CreatePost = &self.data; + let data: &CreatePost = &self; let user = get_user_from_jwt(&data.auth, pool).await?; check_slurs(&data.name)?; @@ -169,7 +170,7 @@ impl Perform for Oper { // Fetch Iframely and pictrs cached image let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) = - fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await; + fetch_iframely_and_pictrs_data(&client, data.url.to_owned()).await; let post_form = PostForm { name: data.name.trim().to_owned(), @@ -217,7 +218,7 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err("couldnt_create_post").into()), }; - updated_post.send_create(&user, &self.client, pool).await?; + updated_post.send_create(&user, &client, pool).await?; // They like their own post by default let like_form = PostLikeForm { @@ -231,7 +232,7 @@ impl Perform for Oper { return Err(APIError::err("couldnt_like_post").into()); } - updated_post.send_like(&user, &self.client, pool).await?; + updated_post.send_like(&user, &client, pool).await?; // Refetch the view let inserted_post_id = inserted_post.id; @@ -259,15 +260,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetPost { type Response = GetPostResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetPost = &self.data; + let data: &GetPost = &self; let user = get_user_from_jwt_opt(&data.auth, pool).await?; let user_id = user.map(|u| u.id); @@ -329,15 +331,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetPosts { type Response = GetPostsResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetPosts = &self.data; + let data: &GetPosts = &self; let user = get_user_from_jwt_opt(&data.auth, pool).await?; let user_id = match &user { @@ -394,15 +397,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for CreatePostLike { type Response = PostResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &CreatePostLike = &self.data; + let data: &CreatePostLike = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Don't do a downvote if site has downvotes disabled @@ -439,12 +443,12 @@ impl Perform for Oper { } if like_form.score == 1 { - post.send_like(&user, &self.client, pool).await?; + post.send_like(&user, &client, pool).await?; } else if like_form.score == -1 { - post.send_dislike(&user, &self.client, pool).await?; + post.send_dislike(&user, &client, pool).await?; } } else { - post.send_undo_like(&user, &self.client, pool).await?; + post.send_undo_like(&user, &client, pool).await?; } let post_id = data.post_id; @@ -473,15 +477,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for EditPost { type Response = PostResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &EditPost = &self.data; + let data: &EditPost = &self; let user = get_user_from_jwt(&data.auth, pool).await?; check_slurs(&data.name)?; @@ -503,7 +508,7 @@ impl Perform for Oper { // Fetch Iframely and Pictrs cached image let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) = - fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await; + fetch_iframely_and_pictrs_data(&client, data.url.to_owned()).await; let post_form = PostForm { name: data.name.trim().to_owned(), @@ -542,7 +547,7 @@ impl Perform for Oper { }; // Send apub update - updated_post.send_update(&user, &self.client, pool).await?; + updated_post.send_update(&user, &client, pool).await?; let edit_id = data.edit_id; let post_view = blocking(pool, move |conn| { @@ -565,15 +570,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for DeletePost { type Response = PostResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &DeletePost = &self.data; + let data: &DeletePost = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let edit_id = data.edit_id; @@ -596,11 +602,9 @@ impl Perform for Oper { // apub updates if deleted { - updated_post.send_delete(&user, &self.client, pool).await?; + updated_post.send_delete(&user, &client, pool).await?; } else { - updated_post - .send_undo_delete(&user, &self.client, pool) - .await?; + updated_post.send_undo_delete(&user, &client, pool).await?; } // Refetch the post @@ -625,15 +629,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for RemovePost { type Response = PostResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &RemovePost = &self.data; + let data: &RemovePost = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let edit_id = data.edit_id; @@ -663,11 +668,9 @@ impl Perform for Oper { // apub updates if removed { - updated_post.send_remove(&user, &self.client, pool).await?; + updated_post.send_remove(&user, &client, pool).await?; } else { - updated_post - .send_undo_remove(&user, &self.client, pool) - .await?; + updated_post.send_undo_remove(&user, &client, pool).await?; } // Refetch the post @@ -693,15 +696,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for LockPost { type Response = PostResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &LockPost = &self.data; + let data: &LockPost = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let edit_id = data.edit_id; @@ -727,7 +731,7 @@ impl Perform for Oper { blocking(pool, move |conn| ModLockPost::create(conn, &form)).await??; // apub updates - updated_post.send_update(&user, &self.client, pool).await?; + updated_post.send_update(&user, &client, pool).await?; // Refetch the post let edit_id = data.edit_id; @@ -751,15 +755,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for StickyPost { type Response = PostResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &StickyPost = &self.data; + let data: &StickyPost = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let edit_id = data.edit_id; @@ -788,7 +793,7 @@ impl Perform for Oper { // Apub updates // TODO stickied should pry work like locked for ease of use - updated_post.send_update(&user, &self.client, pool).await?; + updated_post.send_update(&user, &client, pool).await?; // Refetch the post let edit_id = data.edit_id; @@ -812,15 +817,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for SavePost { type Response = PostResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &SavePost = &self.data; + let data: &SavePost = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let post_saved_form = PostSavedForm { diff --git a/server/src/api/site.rs b/server/src/api/site.rs index 515c3e5be..7bf14d3a7 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -7,7 +7,6 @@ use crate::{ get_user_from_jwt_opt, is_admin, APIError, - Oper, Perform, }, apub::fetcher::search_by_apub_id, @@ -17,6 +16,7 @@ use crate::{ DbPool, LemmyError, }; +use actix_web::client::Client; use lemmy_db::{ category::*, comment_view::*, @@ -156,15 +156,16 @@ pub struct SaveSiteConfig { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for ListCategories { type Response = ListCategoriesResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let _data: &ListCategories = &self.data; + let _data: &ListCategories = &self; let categories = blocking(pool, move |conn| Category::list_all(conn)).await??; @@ -174,15 +175,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetModlog { type Response = GetModlogResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetModlog = &self.data; + let data: &GetModlog = &self; let community_id = data.community_id; let mod_user_id = data.mod_user_id; @@ -248,15 +250,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for CreateSite { type Response = SiteResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &CreateSite = &self.data; + let data: &CreateSite = &self; let user = get_user_from_jwt(&data.auth, pool).await?; @@ -290,14 +293,15 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for EditSite { type Response = SiteResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &EditSite = &self.data; + let data: &EditSite = &self; let user = get_user_from_jwt(&data.auth, pool).await?; check_slurs(&data.name)?; @@ -345,15 +349,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetSite { type Response = GetSiteResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &GetSite = &self.data; + let data: &GetSite = &self; // TODO refactor this a little let res = blocking(pool, move |conn| Site::read(conn, 1)).await?; @@ -370,8 +375,8 @@ impl Perform for Oper { captcha_uuid: None, captcha_answer: None, }; - let login_response = Oper::new(register, self.client.clone()) - .perform(pool, websocket_info.clone()) + let login_response = register + .perform(pool, websocket_info.clone(), client.clone()) .await?; info!("Admin {} created", setup.admin_username); @@ -385,8 +390,8 @@ impl Perform for Oper { enable_nsfw: true, auth: login_response.jwt, }; - Oper::new(create_site, self.client.clone()) - .perform(pool, websocket_info.clone()) + create_site + .perform(pool, websocket_info.clone(), client.clone()) .await?; info!("Site {} created", setup.site_name); Some(blocking(pool, move |conn| SiteView::read(conn)).await??) @@ -440,19 +445,20 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for Search { type Response = SearchResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + client: Client, ) -> Result { - let data: &Search = &self.data; + let data: &Search = &self; dbg!(&data); - match search_by_apub_id(&data.q, &self.client, pool).await { + match search_by_apub_id(&data.q, &client, pool).await { Ok(r) => return Ok(r), Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e), } @@ -604,15 +610,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for TransferSite { type Response = GetSiteResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &TransferSite = &self.data; + let data: &TransferSite = &self; let mut user = get_user_from_jwt(&data.auth, pool).await?; // TODO add a User_::read_safe() for this. @@ -667,15 +674,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetSiteConfig { type Response = GetSiteConfigResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetSiteConfig = &self.data; + let data: &GetSiteConfig = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Only let admins read this @@ -688,15 +696,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for SaveSiteConfig { type Response = GetSiteConfigResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &SaveSiteConfig = &self.data; + let data: &SaveSiteConfig = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Only let admins read this diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 83d8470cd..9bb03ba1a 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -6,7 +6,6 @@ use crate::{ get_user_from_jwt_opt, is_admin, APIError, - Oper, Perform, }, apub::ApubObjectType, @@ -20,6 +19,7 @@ use crate::{ DbPool, LemmyError, }; +use actix_web::client::Client; use bcrypt::verify; use captcha::{gen, Difficulty}; use chrono::Duration; @@ -294,15 +294,16 @@ pub struct UserJoinResponse { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for Login { type Response = LoginResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &Login = &self.data; + let data: &Login = &self; // Fetch that username / email let username_or_email = data.username_or_email.clone(); @@ -329,15 +330,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for Register { type Response = LoginResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &Register = &self.data; + let data: &Register = &self; // Make sure site has open registration if let Ok(site) = blocking(pool, move |conn| SiteView::read(conn)).await? { @@ -498,13 +500,14 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetCaptcha { type Response = GetCaptchaResponse; async fn perform( &self, _pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { let captcha_settings = Settings::get().captcha; @@ -546,15 +549,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for SaveUserSettings { type Response = LoginResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &SaveUserSettings = &self.data; + let data: &SaveUserSettings = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let user_id = user.id; @@ -669,15 +673,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetUserDetails { type Response = GetUserDetailsResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetUserDetails = &self.data; + let data: &GetUserDetails = &self; let user = get_user_from_jwt_opt(&data.auth, pool).await?; let show_nsfw = match &user { @@ -764,15 +769,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for AddAdmin { type Response = AddAdminResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &AddAdmin = &self.data; + let data: &AddAdmin = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Make sure user is an admin @@ -817,15 +823,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for BanUser { type Response = BanUserResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &BanUser = &self.data; + let data: &BanUser = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Make sure user is an admin @@ -875,15 +882,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetReplies { type Response = GetRepliesResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetReplies = &self.data; + let data: &GetReplies = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let sort = SortType::from_str(&data.sort)?; @@ -907,15 +915,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetUserMentions { type Response = GetUserMentionsResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetUserMentions = &self.data; + let data: &GetUserMentions = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let sort = SortType::from_str(&data.sort)?; @@ -939,15 +948,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for MarkUserMentionAsRead { type Response = UserMentionResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &MarkUserMentionAsRead = &self.data; + let data: &MarkUserMentionAsRead = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let user_mention_id = data.user_mention_id; @@ -979,15 +989,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for MarkAllAsRead { type Response = GetRepliesResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &MarkAllAsRead = &self.data; + let data: &MarkAllAsRead = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let user_id = user.id; @@ -1028,15 +1039,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for DeleteAccount { type Response = LoginResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &DeleteAccount = &self.data; + let data: &DeleteAccount = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Verify the password @@ -1090,15 +1102,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for PasswordReset { type Response = PasswordResetResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &PasswordReset = &self.data; + let data: &PasswordReset = &self; // Fetch that email let email = data.email.clone(); @@ -1134,15 +1147,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for PasswordChange { type Response = LoginResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &PasswordChange = &self.data; + let data: &PasswordChange = &self; // Fetch the user_id from the token let token = data.token.clone(); @@ -1175,15 +1189,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for CreatePrivateMessage { type Response = PrivateMessageResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &CreatePrivateMessage = &self.data; + let data: &CreatePrivateMessage = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let hostname = &format!("https://{}", Settings::get().hostname); @@ -1229,7 +1244,7 @@ impl Perform for Oper { }; updated_private_message - .send_create(&user, &self.client, pool) + .send_create(&user, &client, pool) .await?; // Send notifications to the recipient @@ -1274,15 +1289,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for EditPrivateMessage { type Response = PrivateMessageResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &EditPrivateMessage = &self.data; + let data: &EditPrivateMessage = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Checking permissions @@ -1307,7 +1323,7 @@ impl Perform for Oper { // Send the apub update updated_private_message - .send_update(&user, &self.client, pool) + .send_update(&user, &client, pool) .await?; let edit_id = data.edit_id; @@ -1330,15 +1346,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for DeletePrivateMessage { type Response = PrivateMessageResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + client: Client, ) -> Result { - let data: &DeletePrivateMessage = &self.data; + let data: &DeletePrivateMessage = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Checking permissions @@ -1364,11 +1381,11 @@ impl Perform for Oper { // Send the apub update if data.deleted { updated_private_message - .send_delete(&user, &self.client, pool) + .send_delete(&user, &client, pool) .await?; } else { updated_private_message - .send_undo_delete(&user, &self.client, pool) + .send_undo_delete(&user, &client, pool) .await?; } @@ -1392,15 +1409,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for MarkPrivateMessageAsRead { type Response = PrivateMessageResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &MarkPrivateMessageAsRead = &self.data; + let data: &MarkPrivateMessageAsRead = &self; let user = get_user_from_jwt(&data.auth, pool).await?; // Checking permissions @@ -1445,15 +1463,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for GetPrivateMessages { type Response = PrivateMessagesResponse; async fn perform( &self, pool: &DbPool, _websocket_info: Option, + _client: Client, ) -> Result { - let data: &GetPrivateMessages = &self.data; + let data: &GetPrivateMessages = &self; let user = get_user_from_jwt(&data.auth, pool).await?; let user_id = user.id; @@ -1474,15 +1493,16 @@ impl Perform for Oper { } #[async_trait::async_trait(?Send)] -impl Perform for Oper { +impl Perform for UserJoin { type Response = UserJoinResponse; async fn perform( &self, pool: &DbPool, websocket_info: Option, + _client: Client, ) -> Result { - let data: &UserJoin = &self.data; + let data: &UserJoin = &self; let user = get_user_from_jwt(&data.auth, pool).await?; if let Some(ws) = websocket_info { diff --git a/server/src/routes/api.rs b/server/src/routes/api.rs index f524cf411..17e7e591c 100644 --- a/server/src/routes/api.rs +++ b/server/src/routes/api.rs @@ -1,5 +1,5 @@ use crate::{ - api::{comment::*, community::*, post::*, site::*, user::*, Oper, Perform}, + api::{comment::*, community::*, post::*, site::*, user::*, Perform}, rate_limit::RateLimit, routes::{ChatServerParam, DbPoolParam}, websocket::WebsocketInfo, @@ -179,7 +179,7 @@ async fn perform( chat_server: ChatServerParam, ) -> Result where - Oper: Perform, + Request: Perform, Request: Send + 'static, { let ws_info = WebsocketInfo { @@ -187,10 +187,8 @@ where id: None, }; - let oper: Oper = Oper::new(data, client.clone()); - - let res = oper - .perform(&db, Some(ws_info)) + let res = data + .perform(&db, Some(ws_info), client.clone()) .await .map(|json| HttpResponse::Ok().json(json)) .map_err(ErrorBadRequest)?; @@ -204,8 +202,7 @@ async fn route_get( chat_server: ChatServerParam, ) -> Result where - Data: Serialize + Send + 'static, - Oper: Perform, + Data: Serialize + Send + 'static + Perform, { perform::(data.0, &client, db, chat_server).await } @@ -217,8 +214,7 @@ async fn route_post( chat_server: ChatServerParam, ) -> Result where - Data: Serialize + Send + 'static, - Oper: Perform, + Data: Serialize + Send + 'static + Perform, { perform::(data.0, &client, db, chat_server).await } diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 6c3eed4be..a6169fab9 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -556,7 +556,7 @@ struct Args<'a> { async fn do_user_operation<'a, 'b, Data>(args: Args<'b>) -> Result where for<'de> Data: Deserialize<'de> + 'a, - Oper: Perform, + Data: Perform, { let Args { client, @@ -581,9 +581,7 @@ where let fut = async move { let pool = pool.clone(); let parsed_data: Data = serde_json::from_str(&data)?; - let res = Oper::new(parsed_data, client) - .perform(&pool, Some(ws_info)) - .await?; + let res = parsed_data.perform(&pool, Some(ws_info), client).await?; to_json_string(&op, &res) };