1 use actix_web::{web, web::Data};
3 use lemmy_api_common::{
12 use lemmy_utils::{error::LemmyError, utils::check_slurs, ConnectionId};
13 use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
14 use serde::Deserialize;
23 mod private_message_report;
27 #[async_trait::async_trait(?Send)]
29 type Response: serde::ser::Serialize + Send;
33 context: &Data<LemmyContext>,
34 websocket_id: Option<ConnectionId>,
35 ) -> Result<Self::Response, LemmyError>;
38 pub async fn match_websocket_operation(
39 context: LemmyContext,
43 ) -> Result<String, LemmyError> {
46 UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await,
47 UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
48 UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
49 UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
50 UserOperation::GetUnreadRegistrationApplicationCount => {
51 do_websocket_operation::<GetUnreadRegistrationApplicationCount>(context, id, op, data).await
53 UserOperation::ListRegistrationApplications => {
54 do_websocket_operation::<ListRegistrationApplications>(context, id, op, data).await
56 UserOperation::ApproveRegistrationApplication => {
57 do_websocket_operation::<ApproveRegistrationApplication>(context, id, op, data).await
59 UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
60 UserOperation::GetBannedPersons => {
61 do_websocket_operation::<GetBannedPersons>(context, id, op, data).await
63 UserOperation::BlockPerson => {
64 do_websocket_operation::<BlockPerson>(context, id, op, data).await
66 UserOperation::GetPersonMentions => {
67 do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
69 UserOperation::MarkPersonMentionAsRead => {
70 do_websocket_operation::<MarkPersonMentionAsRead>(context, id, op, data).await
72 UserOperation::MarkCommentReplyAsRead => {
73 do_websocket_operation::<MarkCommentReplyAsRead>(context, id, op, data).await
75 UserOperation::MarkAllAsRead => {
76 do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
78 UserOperation::PasswordReset => {
79 do_websocket_operation::<PasswordReset>(context, id, op, data).await
81 UserOperation::PasswordChange => {
82 do_websocket_operation::<PasswordChangeAfterReset>(context, id, op, data).await
84 UserOperation::UserJoin => do_websocket_operation::<UserJoin>(context, id, op, data).await,
85 UserOperation::PostJoin => do_websocket_operation::<PostJoin>(context, id, op, data).await,
86 UserOperation::CommunityJoin => {
87 do_websocket_operation::<CommunityJoin>(context, id, op, data).await
89 UserOperation::ModJoin => do_websocket_operation::<ModJoin>(context, id, op, data).await,
90 UserOperation::SaveUserSettings => {
91 do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
93 UserOperation::ChangePassword => {
94 do_websocket_operation::<ChangePassword>(context, id, op, data).await
96 UserOperation::GetReportCount => {
97 do_websocket_operation::<GetReportCount>(context, id, op, data).await
99 UserOperation::GetUnreadCount => {
100 do_websocket_operation::<GetUnreadCount>(context, id, op, data).await
102 UserOperation::VerifyEmail => {
103 do_websocket_operation::<VerifyEmail>(context, id, op, data).await
106 // Private Message ops
107 UserOperation::MarkPrivateMessageAsRead => {
108 do_websocket_operation::<MarkPrivateMessageAsRead>(context, id, op, data).await
110 UserOperation::CreatePrivateMessageReport => {
111 do_websocket_operation::<CreatePrivateMessageReport>(context, id, op, data).await
113 UserOperation::ResolvePrivateMessageReport => {
114 do_websocket_operation::<ResolvePrivateMessageReport>(context, id, op, data).await
116 UserOperation::ListPrivateMessageReports => {
117 do_websocket_operation::<ListPrivateMessageReports>(context, id, op, data).await
121 UserOperation::GetModlog => do_websocket_operation::<GetModlog>(context, id, op, data).await,
122 UserOperation::PurgePerson => {
123 do_websocket_operation::<PurgePerson>(context, id, op, data).await
125 UserOperation::PurgeCommunity => {
126 do_websocket_operation::<PurgeCommunity>(context, id, op, data).await
128 UserOperation::PurgePost => do_websocket_operation::<PurgePost>(context, id, op, data).await,
129 UserOperation::PurgeComment => {
130 do_websocket_operation::<PurgeComment>(context, id, op, data).await
132 UserOperation::Search => do_websocket_operation::<Search>(context, id, op, data).await,
133 UserOperation::ResolveObject => {
134 do_websocket_operation::<ResolveObject>(context, id, op, data).await
136 UserOperation::TransferCommunity => {
137 do_websocket_operation::<TransferCommunity>(context, id, op, data).await
139 UserOperation::LeaveAdmin => do_websocket_operation::<LeaveAdmin>(context, id, op, data).await,
142 UserOperation::FollowCommunity => {
143 do_websocket_operation::<FollowCommunity>(context, id, op, data).await
145 UserOperation::BlockCommunity => {
146 do_websocket_operation::<BlockCommunity>(context, id, op, data).await
148 UserOperation::BanFromCommunity => {
149 do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
151 UserOperation::AddModToCommunity => {
152 do_websocket_operation::<AddModToCommunity>(context, id, op, data).await
156 UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
157 UserOperation::StickyPost => do_websocket_operation::<StickyPost>(context, id, op, data).await,
158 UserOperation::CreatePostLike => {
159 do_websocket_operation::<CreatePostLike>(context, id, op, data).await
161 UserOperation::MarkPostAsRead => {
162 do_websocket_operation::<MarkPostAsRead>(context, id, op, data).await
164 UserOperation::SavePost => do_websocket_operation::<SavePost>(context, id, op, data).await,
165 UserOperation::CreatePostReport => {
166 do_websocket_operation::<CreatePostReport>(context, id, op, data).await
168 UserOperation::ListPostReports => {
169 do_websocket_operation::<ListPostReports>(context, id, op, data).await
171 UserOperation::ResolvePostReport => {
172 do_websocket_operation::<ResolvePostReport>(context, id, op, data).await
174 UserOperation::GetSiteMetadata => {
175 do_websocket_operation::<GetSiteMetadata>(context, id, op, data).await
179 UserOperation::SaveComment => {
180 do_websocket_operation::<SaveComment>(context, id, op, data).await
182 UserOperation::CreateCommentLike => {
183 do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
185 UserOperation::CreateCommentReport => {
186 do_websocket_operation::<CreateCommentReport>(context, id, op, data).await
188 UserOperation::ListCommentReports => {
189 do_websocket_operation::<ListCommentReports>(context, id, op, data).await
191 UserOperation::ResolveCommentReport => {
192 do_websocket_operation::<ResolveCommentReport>(context, id, op, data).await
197 async fn do_websocket_operation<'a, 'b, Data>(
198 context: LemmyContext,
202 ) -> Result<String, LemmyError>
204 for<'de> Data: Deserialize<'de> + 'a,
207 let parsed_data: Data = serde_json::from_str(data)?;
208 let res = parsed_data
209 .perform(&web::Data::new(context), Some(id))
211 serialize_websocket_message(&op, &res)
214 /// Converts the captcha to a base64 encoded wav audio file
215 pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
216 let letters = captcha.as_wav();
218 let mut concat_letters: Vec<u8> = Vec::new();
220 for letter in letters {
221 let bytes = letter.unwrap_or_default();
222 concat_letters.extend(bytes);
226 base64::encode(concat_letters)
229 /// Check size of report and remove whitespace
230 pub(crate) fn check_report_reason(reason: &str, context: &LemmyContext) -> Result<(), LemmyError> {
231 check_slurs(reason, &context.settings().slur_regex())?;
232 if reason.is_empty() {
233 return Err(LemmyError::from_message("report_reason_required"));
235 if reason.chars().count() > 1000 {
236 return Err(LemmyError::from_message("report_too_long"));
243 use lemmy_api_common::utils::check_validator_time;
244 use lemmy_db_schema::{
246 local_user::{LocalUser, LocalUserForm},
247 person::{Person, PersonForm},
251 utils::establish_unpooled_connection,
253 use lemmy_utils::{claims::Claims, settings::SETTINGS};
256 fn test_should_not_validate_user_token_after_password_change() {
257 let conn = &mut establish_unpooled_connection();
258 let secret = Secret::init(conn).unwrap();
259 let settings = &SETTINGS.to_owned();
261 let new_person = PersonForm {
262 name: "Gerry9812".into(),
263 public_key: Some("pubkey".to_string()),
264 ..PersonForm::default()
267 let inserted_person = Person::create(conn, &new_person).unwrap();
269 let local_user_form = LocalUserForm {
270 person_id: Some(inserted_person.id),
271 password_encrypted: Some("123456".to_string()),
272 ..LocalUserForm::default()
275 let inserted_local_user = LocalUser::create(conn, &local_user_form).unwrap();
277 let jwt = Claims::jwt(
278 inserted_local_user.id.0,
283 let claims = Claims::decode(&jwt, &secret.jwt_secret).unwrap().claims;
284 let check = check_validator_time(&inserted_local_user.validator_time, &claims);
285 assert!(check.is_ok());
287 // The check should fail, since the validator time is now newer than the jwt issue time
288 let updated_local_user =
289 LocalUser::update_password(conn, inserted_local_user.id, "password111").unwrap();
290 let check_after = check_validator_time(&updated_local_user.validator_time, &claims);
291 assert!(check_after.is_err());
293 let num_deleted = Person::delete(conn, inserted_person.id).unwrap();
294 assert_eq!(1, num_deleted);