2020-09-24 13:53:21 +00:00
|
|
|
use actix_web::{web, web::Data};
|
2021-03-25 19:19:40 +00:00
|
|
|
use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
|
|
|
|
use lemmy_utils::{ConnectionId, LemmyError};
|
2020-09-24 13:53:21 +00:00
|
|
|
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
|
|
|
|
use serde::Deserialize;
|
2021-03-13 18:16:35 +00:00
|
|
|
use std::{env, process::Command};
|
2020-09-24 13:53:21 +00:00
|
|
|
|
2021-03-25 19:19:40 +00:00
|
|
|
mod comment;
|
|
|
|
mod comment_report;
|
|
|
|
mod community;
|
|
|
|
mod local_user;
|
|
|
|
mod post;
|
|
|
|
mod post_report;
|
|
|
|
mod private_message;
|
|
|
|
mod site;
|
|
|
|
mod websocket;
|
2020-09-24 13:53:21 +00:00
|
|
|
|
|
|
|
#[async_trait::async_trait(?Send)]
|
|
|
|
pub trait Perform {
|
|
|
|
type Response: serde::ser::Serialize + Send;
|
|
|
|
|
|
|
|
async fn perform(
|
|
|
|
&self,
|
|
|
|
context: &Data<LemmyContext>,
|
|
|
|
websocket_id: Option<ConnectionId>,
|
|
|
|
) -> Result<Self::Response, LemmyError>;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn match_websocket_operation(
|
|
|
|
context: LemmyContext,
|
|
|
|
id: ConnectionId,
|
|
|
|
op: UserOperation,
|
|
|
|
data: &str,
|
|
|
|
) -> Result<String, LemmyError> {
|
|
|
|
match op {
|
|
|
|
// User ops
|
|
|
|
UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await,
|
|
|
|
UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
|
|
|
|
UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
|
|
|
|
UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
|
2021-03-10 22:33:55 +00:00
|
|
|
UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
|
|
|
|
UserOperation::GetPersonMentions => {
|
|
|
|
do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
|
2020-09-24 13:53:21 +00:00
|
|
|
}
|
2021-03-10 22:33:55 +00:00
|
|
|
UserOperation::MarkPersonMentionAsRead => {
|
|
|
|
do_websocket_operation::<MarkPersonMentionAsRead>(context, id, op, data).await
|
2020-09-24 13:53:21 +00:00
|
|
|
}
|
|
|
|
UserOperation::MarkAllAsRead => {
|
|
|
|
do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::PasswordReset => {
|
|
|
|
do_websocket_operation::<PasswordReset>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::PasswordChange => {
|
|
|
|
do_websocket_operation::<PasswordChange>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::UserJoin => do_websocket_operation::<UserJoin>(context, id, op, data).await,
|
|
|
|
UserOperation::PostJoin => do_websocket_operation::<PostJoin>(context, id, op, data).await,
|
|
|
|
UserOperation::CommunityJoin => {
|
|
|
|
do_websocket_operation::<CommunityJoin>(context, id, op, data).await
|
|
|
|
}
|
2020-11-04 02:15:11 +00:00
|
|
|
UserOperation::ModJoin => do_websocket_operation::<ModJoin>(context, id, op, data).await,
|
2020-09-24 13:53:21 +00:00
|
|
|
UserOperation::SaveUserSettings => {
|
|
|
|
do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
|
|
|
|
}
|
2020-10-25 02:59:13 +00:00
|
|
|
UserOperation::GetReportCount => {
|
|
|
|
do_websocket_operation::<GetReportCount>(context, id, op, data).await
|
|
|
|
}
|
2020-09-24 13:53:21 +00:00
|
|
|
|
|
|
|
// Private Message ops
|
|
|
|
UserOperation::MarkPrivateMessageAsRead => {
|
|
|
|
do_websocket_operation::<MarkPrivateMessageAsRead>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
|
|
|
|
// Site ops
|
|
|
|
UserOperation::GetModlog => do_websocket_operation::<GetModlog>(context, id, op, data).await,
|
|
|
|
UserOperation::GetSiteConfig => {
|
|
|
|
do_websocket_operation::<GetSiteConfig>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::SaveSiteConfig => {
|
|
|
|
do_websocket_operation::<SaveSiteConfig>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::Search => do_websocket_operation::<Search>(context, id, op, data).await,
|
|
|
|
UserOperation::TransferCommunity => {
|
|
|
|
do_websocket_operation::<TransferCommunity>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::TransferSite => {
|
|
|
|
do_websocket_operation::<TransferSite>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
|
|
|
|
// Community ops
|
|
|
|
UserOperation::FollowCommunity => {
|
|
|
|
do_websocket_operation::<FollowCommunity>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::GetFollowedCommunities => {
|
|
|
|
do_websocket_operation::<GetFollowedCommunities>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::BanFromCommunity => {
|
|
|
|
do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::AddModToCommunity => {
|
|
|
|
do_websocket_operation::<AddModToCommunity>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
|
|
|
|
// Post ops
|
|
|
|
UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
|
|
|
|
UserOperation::StickyPost => do_websocket_operation::<StickyPost>(context, id, op, data).await,
|
|
|
|
UserOperation::CreatePostLike => {
|
|
|
|
do_websocket_operation::<CreatePostLike>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::SavePost => do_websocket_operation::<SavePost>(context, id, op, data).await,
|
2020-10-25 02:59:13 +00:00
|
|
|
UserOperation::CreatePostReport => {
|
|
|
|
do_websocket_operation::<CreatePostReport>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::ListPostReports => {
|
|
|
|
do_websocket_operation::<ListPostReports>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::ResolvePostReport => {
|
|
|
|
do_websocket_operation::<ResolvePostReport>(context, id, op, data).await
|
|
|
|
}
|
2020-09-24 13:53:21 +00:00
|
|
|
|
|
|
|
// Comment ops
|
|
|
|
UserOperation::MarkCommentAsRead => {
|
|
|
|
do_websocket_operation::<MarkCommentAsRead>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::SaveComment => {
|
|
|
|
do_websocket_operation::<SaveComment>(context, id, op, data).await
|
|
|
|
}
|
|
|
|
UserOperation::CreateCommentLike => {
|
|
|
|
do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
|
|
|
|
}
|
2020-10-25 02:59:13 +00:00
|
|
|
UserOperation::CreateCommentReport => {
|
|
|
|
do_websocket_operation::<CreateCommentReport>(context, id, op, data).await
|
2020-10-21 00:31:01 +00:00
|
|
|
}
|
2020-10-25 02:59:13 +00:00
|
|
|
UserOperation::ListCommentReports => {
|
|
|
|
do_websocket_operation::<ListCommentReports>(context, id, op, data).await
|
2020-10-13 23:32:35 +00:00
|
|
|
}
|
2020-10-25 02:59:13 +00:00
|
|
|
UserOperation::ResolveCommentReport => {
|
|
|
|
do_websocket_operation::<ResolveCommentReport>(context, id, op, data).await
|
2020-10-13 23:32:35 +00:00
|
|
|
}
|
2020-09-24 13:53:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn do_websocket_operation<'a, 'b, Data>(
|
|
|
|
context: LemmyContext,
|
|
|
|
id: ConnectionId,
|
|
|
|
op: UserOperation,
|
|
|
|
data: &str,
|
|
|
|
) -> Result<String, LemmyError>
|
|
|
|
where
|
|
|
|
for<'de> Data: Deserialize<'de> + 'a,
|
|
|
|
Data: Perform,
|
|
|
|
{
|
|
|
|
let parsed_data: Data = serde_json::from_str(&data)?;
|
|
|
|
let res = parsed_data
|
|
|
|
.perform(&web::Data::new(context), Some(id))
|
|
|
|
.await?;
|
|
|
|
serialize_websocket_message(&op, &res)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn captcha_espeak_wav_base64(captcha: &str) -> Result<String, LemmyError> {
|
|
|
|
let mut built_text = String::new();
|
|
|
|
|
|
|
|
// Building proper speech text for espeak
|
|
|
|
for mut c in captcha.chars() {
|
|
|
|
let new_str = if c.is_alphabetic() {
|
|
|
|
if c.is_lowercase() {
|
|
|
|
c.make_ascii_uppercase();
|
|
|
|
format!("lower case {} ... ", c)
|
|
|
|
} else {
|
|
|
|
c.make_ascii_uppercase();
|
|
|
|
format!("capital {} ... ", c)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
format!("{} ...", c)
|
|
|
|
};
|
|
|
|
|
|
|
|
built_text.push_str(&new_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
espeak_wav_base64(&built_text)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
|
|
|
|
// Make a temp file path
|
|
|
|
let uuid = uuid::Uuid::new_v4().to_string();
|
2021-03-13 18:16:35 +00:00
|
|
|
let file_path = format!(
|
|
|
|
"{}/lemmy_espeak_{}.wav",
|
|
|
|
env::temp_dir().to_string_lossy(),
|
|
|
|
&uuid
|
|
|
|
);
|
2020-09-24 13:53:21 +00:00
|
|
|
|
|
|
|
// Write the wav file
|
|
|
|
Command::new("espeak")
|
|
|
|
.arg("-w")
|
|
|
|
.arg(&file_path)
|
|
|
|
.arg(text)
|
|
|
|
.status()?;
|
|
|
|
|
|
|
|
// Read the wav file bytes
|
|
|
|
let bytes = std::fs::read(&file_path)?;
|
|
|
|
|
|
|
|
// Delete the file
|
|
|
|
std::fs::remove_file(file_path)?;
|
|
|
|
|
|
|
|
// Convert to base64
|
|
|
|
let base64 = base64::encode(bytes);
|
|
|
|
|
|
|
|
Ok(base64)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-03-25 19:30:15 +00:00
|
|
|
use crate::captcha_espeak_wav_base64;
|
2021-03-25 19:19:40 +00:00
|
|
|
use lemmy_api_common::check_validator_time;
|
2021-03-19 04:31:49 +00:00
|
|
|
use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
|
|
|
|
use lemmy_db_schema::source::{
|
|
|
|
local_user::{LocalUser, LocalUserForm},
|
|
|
|
person::{Person, PersonForm},
|
2021-03-13 18:16:35 +00:00
|
|
|
};
|
|
|
|
use lemmy_utils::claims::Claims;
|
|
|
|
|
2021-03-19 04:31:49 +00:00
|
|
|
#[test]
|
|
|
|
fn test_should_not_validate_user_token_after_password_change() {
|
|
|
|
let conn = establish_unpooled_connection();
|
2021-03-13 18:16:35 +00:00
|
|
|
|
2021-03-19 04:31:49 +00:00
|
|
|
let new_person = PersonForm {
|
|
|
|
name: "Gerry9812".into(),
|
2021-03-20 20:59:07 +00:00
|
|
|
..PersonForm::default()
|
2021-03-13 18:16:35 +00:00
|
|
|
};
|
|
|
|
|
2021-03-19 04:31:49 +00:00
|
|
|
let inserted_person = Person::create(&conn, &new_person).unwrap();
|
2021-03-13 18:16:35 +00:00
|
|
|
|
2021-03-19 04:31:49 +00:00
|
|
|
let local_user_form = LocalUserForm {
|
|
|
|
person_id: inserted_person.id,
|
|
|
|
password_encrypted: "123456".to_string(),
|
2021-03-20 20:59:07 +00:00
|
|
|
..LocalUserForm::default()
|
2021-03-19 04:31:49 +00:00
|
|
|
};
|
2021-03-13 20:36:40 +00:00
|
|
|
|
2021-03-19 04:31:49 +00:00
|
|
|
let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap();
|
2021-03-13 18:16:35 +00:00
|
|
|
|
2021-03-19 04:31:49 +00:00
|
|
|
let jwt = Claims::jwt(inserted_local_user.id.0).unwrap();
|
|
|
|
let claims = Claims::decode(&jwt).unwrap().claims;
|
|
|
|
let check = check_validator_time(&inserted_local_user.validator_time, &claims);
|
|
|
|
assert!(check.is_ok());
|
2021-03-13 18:16:35 +00:00
|
|
|
|
2021-03-19 04:31:49 +00:00
|
|
|
// The check should fail, since the validator time is now newer than the jwt issue time
|
|
|
|
let updated_local_user =
|
|
|
|
LocalUser::update_password(&conn, inserted_local_user.id, &"password111").unwrap();
|
|
|
|
let check_after = check_validator_time(&updated_local_user.validator_time, &claims);
|
|
|
|
assert!(check_after.is_err());
|
2021-03-13 18:16:35 +00:00
|
|
|
|
2021-03-19 04:31:49 +00:00
|
|
|
let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
|
|
|
|
assert_eq!(1, num_deleted);
|
2021-03-13 18:16:35 +00:00
|
|
|
}
|
2020-09-24 13:53:21 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_espeak() {
|
|
|
|
assert!(captcha_espeak_wav_base64("WxRt2l").is_ok())
|
|
|
|
}
|
|
|
|
}
|