diff options
Diffstat (limited to 'server/src')
52 files changed, 710 insertions, 7489 deletions
diff --git a/server/src/api/claims.rs b/server/src/api/claims.rs new file mode 100644 index 00000000..eec9d1a7 --- /dev/null +++ b/server/src/api/claims.rs @@ -0,0 +1,73 @@ +use diesel::{result::Error, PgConnection}; +use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation}; +use lemmy_db::{user::User_, Crud}; +use lemmy_utils::{is_email_regex, settings::Settings}; +use serde::{Deserialize, Serialize}; + +type Jwt = String; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub id: i32, + pub username: String, + pub iss: String, + pub show_nsfw: bool, + pub theme: String, + pub default_sort_type: i16, + pub default_listing_type: i16, + pub lang: String, + pub avatar: Option<String>, + pub show_avatars: bool, +} + +impl Claims { + pub fn decode(jwt: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> { + let v = Validation { + validate_exp: false, + ..Validation::default() + }; + decode::<Claims>( + &jwt, + &DecodingKey::from_secret(Settings::get().jwt_secret.as_ref()), + &v, + ) + } + + pub fn jwt(user: User_, hostname: String) -> Jwt { + let my_claims = Claims { + id: user.id, + username: user.name.to_owned(), + iss: hostname, + show_nsfw: user.show_nsfw, + theme: user.theme.to_owned(), + default_sort_type: user.default_sort_type, + default_listing_type: user.default_listing_type, + lang: user.lang.to_owned(), + avatar: user.avatar.to_owned(), + show_avatars: user.show_avatars.to_owned(), + }; + encode( + &Header::default(), + &my_claims, + &EncodingKey::from_secret(Settings::get().jwt_secret.as_ref()), + ) + .unwrap() + } + + // TODO: move these into user? + pub fn find_by_email_or_username( + conn: &PgConnection, + username_or_email: &str, + ) -> Result<User_, Error> { + if is_email_regex(username_or_email) { + User_::find_by_email(conn, username_or_email) + } else { + User_::find_by_username(conn, username_or_email) + } + } + + pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result<User_, Error> { + let claims: Claims = Claims::decode(&jwt).expect("Invalid token").claims; + User_::read(&conn, claims.id) + } +} diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index c7406b37..2007542f 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -1,28 +1,7 @@ use crate::{ - api::{APIError, Oper, Perform}, + api::{claims::Claims, APIError, Oper, Perform}, apub::{ApubLikeableType, ApubObjectType}, blocking, - db::{ - comment::*, - comment_view::*, - community_view::*, - moderator::*, - post::*, - site_view::*, - user::*, - user_mention::*, - user_view::*, - Crud, - Likeable, - ListingType, - Saveable, - SortType, - }, - naive_now, - remove_slurs, - scrape_text_for_mentions, - send_email, - settings::Settings, websocket::{ server::{JoinCommunityRoom, SendComment}, UserOperation, @@ -30,6 +9,31 @@ use crate::{ }, DbPool, LemmyError, +}; +use lemmy_db::{ + comment::*, + comment_view::*, + community_view::*, + moderator::*, + naive_now, + post::*, + site_view::*, + user::*, + user_mention::*, + user_view::*, + Crud, + Likeable, + ListingType, + Saveable, + SortType, +}; +use lemmy_utils::{ + make_apub_endpoint, + remove_slurs, + scrape_text_for_mentions, + send_email, + settings::Settings, + EndpointType, MentionData, }; use log::error; @@ -155,7 +159,9 @@ impl Perform for Oper<CreateComment> { let inserted_comment_id = inserted_comment.id; let updated_comment: Comment = match blocking(pool, move |conn| { - Comment::update_ap_id(&conn, inserted_comment_id) + let apub_id = + make_apub_endpoint(EndpointType::Comment, &inserted_comment_id.to_string()).to_string(); + Comment::update_ap_id(&conn, inserted_comment_id, apub_id) }) .await? { diff --git a/server/src/api/community.rs b/server/src/api/community.rs index 02071c57..e703dcf4 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -1,26 +1,24 @@ use super::*; use crate::{ - api::{APIError, Oper, Perform}, - apub::{ - extensions::signatures::generate_actor_keypair, - make_apub_endpoint, - ActorType, - EndpointType, - }, + api::{claims::Claims, APIError, Oper, Perform}, + apub::ActorType, blocking, - db::{Bannable, Crud, Followable, Joinable, SortType}, - is_valid_community_name, - naive_from_unix, - naive_now, - slur_check, - slurs_vec_to_str, websocket::{ server::{JoinCommunityRoom, SendCommunityRoomMessage}, UserOperation, WebsocketInfo, }, DbPool, - LemmyError, +}; +use lemmy_db::{naive_now, Bannable, Crud, Followable, Joinable, SortType}; +use lemmy_utils::{ + generate_actor_keypair, + is_valid_community_name, + make_apub_endpoint, + naive_from_unix, + slur_check, + slurs_vec_to_str, + EndpointType, }; use serde::{Deserialize, Serialize}; use std::str::FromStr; diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 6df9909c..bb65815a 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -1,11 +1,8 @@ -use crate::{ - db::{community::*, community_view::*, moderator::*, site::*, user::*, user_view::*}, - websocket::WebsocketInfo, - DbPool, - LemmyError, -}; +use crate::{websocket::WebsocketInfo, DbPool, LemmyError}; use actix_web::client::Client; +use lemmy_db::{community::*, community_view::*, moderator::*, site::*, user::*, user_view::*}; +pub mod claims; pub mod comment; pub mod community; pub mod post; diff --git a/server/src/api/post.rs b/server/src/api/post.rs index 840f1530..cbdb976c 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -1,27 +1,8 @@ use crate::{ - api::{APIError, Oper, Perform}, + api::{claims::Claims, APIError, Oper, Perform}, apub::{ApubLikeableType, ApubObjectType}, blocking, - db::{ - comment_view::*, - community_view::*, - moderator::*, - post::*, - post_view::*, - site::*, - site_view::*, - user::*, - user_view::*, - Crud, - Likeable, - ListingType, - Saveable, - SortType, - }, fetch_iframely_and_pictrs_data, - naive_now, - slur_check, - slurs_vec_to_str, websocket::{ server::{JoinCommunityRoom, JoinPostRoom, SendPost}, UserOperation, @@ -30,6 +11,24 @@ use crate::{ DbPool, LemmyError, }; +use lemmy_db::{ + comment_view::*, + community_view::*, + moderator::*, + naive_now, + post::*, + post_view::*, + site::*, + site_view::*, + user::*, + user_view::*, + Crud, + Likeable, + ListingType, + Saveable, + SortType, +}; +use lemmy_utils::{is_valid_post_title, make_apub_endpoint, slur_check, slurs_vec_to_str, EndpointType}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -136,6 +135,10 @@ impl Perform for Oper<CreatePost> { } } + if !is_valid_post_title(&data.name) { + return Err(APIError::err("invalid_post_title").into()); + } + let user_id = claims.id; // Check for a community ban @@ -157,7 +160,7 @@ impl Perform for Oper<CreatePost> { fetch_iframely_and_pictrs_data(&self.client, data.url.to_owned()).await; let post_form = PostForm { - name: data.name.to_owned(), + name: data.name.trim().to_owned(), url: data.url.to_owned(), body: data.body.to_owned(), community_id: data.community_id, @@ -191,11 +194,16 @@ impl Perform for Oper<CreatePost> { }; let inserted_post_id = inserted_post.id; - let updated_post = - match blocking(pool, move |conn| Post::update_ap_id(conn, inserted_post_id)).await? { - Ok(post) => post, - Err(_e) => return Err(APIError::err("couldnt_create_post").into()), - }; + let updated_post = match blocking(pool, move |conn| { + let apub_id = + make_apub_endpoint(EndpointType::Post, &inserted_post_id.to_string()).to_string(); + Post::update_ap_id(conn, inserted_post_id, apub_id) + }) + .await? + { + Ok(post) => post, + Err(_e) => return Err(APIError::err("couldnt_create_post").into()), + }; updated_post.send_create(&user, &self.client, pool).await?; @@ -512,6 +520,10 @@ impl Perform for Oper<EditPost> { } } + if !is_valid_post_title(&data.name) { + return Err(APIError::err("invalid_post_title").into()); + } + let claims = match Claims::decode(&data.auth) { Ok(claims) => claims.claims, Err(_e) => return Err(APIError::err("not_logged_in").into()), @@ -561,7 +573,7 @@ impl Perform for Oper<EditPost> { let read_post = blocking(pool, move |conn| Post::read(conn, edit_id)).await??; let post_form = PostForm { - name: data.name.to_owned(), + name: data.name.trim().to_owned(), url: data.url.to_owned(), body: data.body.to_owned(), creator_id: data.creator_id.to_owned(), diff --git a/server/src/api/site.rs b/server/src/api/site.rs index f45561a8..241a80e3 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -1,31 +1,28 @@ use super::user::Register; use crate::{ - api::{APIError, Oper, Perform}, + api::{claims::Claims, APIError, Oper, Perform}, apub::fetcher::search_by_apub_id, blocking, - db::{ - category::*, - comment_view::*, - community_view::*, - moderator::*, - moderator_views::*, - post_view::*, - site::*, - site_view::*, - user::*, - user_view::*, - Crud, - SearchType, - SortType, - }, - naive_now, - settings::Settings, - slur_check, - slurs_vec_to_str, websocket::{server::SendAllMessage, UserOperation, WebsocketInfo}, DbPool, LemmyError, }; +use lemmy_db::{ + category::*, + comment_view::*, + community_view::*, + moderator::*, + moderator_views::*, + naive_now, + post_view::*, + site::*, + site_view::*, + user_view::*, + Crud, + SearchType, + SortType, +}; +use lemmy_utils::{settings::Settings, slur_check, slurs_vec_to_str}; use log::{debug, info}; use serde::{Deserialize, Serialize}; use std::str::FromStr; diff --git a/server/src/api/user.rs b/server/src/api/user.rs index a4e47e41..9f33843f 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -1,53 +1,53 @@ use crate::{ - api::{APIError, Oper, Perform}, - apub::{ - extensions::signatures::generate_actor_keypair, - make_apub_endpoint, - ApubObjectType, - EndpointType, - }, + api::{claims::Claims, APIError, Oper, Perform}, + apub::ApubObjectType, blocking, - db::{ - comment::*, - comment_view::*, - community::*, - community_view::*, - moderator::*, - password_reset_request::*, - post::*, - post_view::*, - private_message::*, - private_message_view::*, - site::*, - site_view::*, - user::*, - user_mention::*, - user_mention_view::*, - user_view::*, - Crud, - Followable, - Joinable, - ListingType, - SortType, + websocket::{ + server::{JoinUserRoom, SendAllMessage, SendUserRoomMessage}, + UserOperation, + WebsocketInfo, }, + DbPool, + LemmyError, +}; +use bcrypt::verify; +use lemmy_db::{ + comment::*, + comment_view::*, + community::*, + community_view::*, + moderator::*, + naive_now, + password_reset_request::*, + post::*, + post_view::*, + private_message::*, + private_message_view::*, + site::*, + site_view::*, + user::*, + user_mention::*, + user_mention_view::*, + user_view::*, + Crud, + Followable, + Joinable, + ListingType, + SortType, +}; +use lemmy_utils::{ + generate_actor_keypair, generate_random_string, is_valid_username, + make_apub_endpoint, naive_from_unix, - naive_now, remove_slurs, send_email, settings::Settings, slur_check, slurs_vec_to_str, - websocket::{ - server::{JoinUserRoom, SendAllMessage, SendUserRoomMessage}, - UserOperation, - WebsocketInfo, - }, - DbPool, - LemmyError, + EndpointType, }; -use bcrypt::verify; use log::error; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -264,7 +264,7 @@ impl Perform for Oper<Login> { // Fetch that username / email let username_or_email = data.username_or_email.clone(); let user = match blocking(pool, move |conn| { - User_::find_by_email_or_username(conn, &username_or_email) + Claims::find_by_email_or_username(conn, &username_or_email) }) .await? { @@ -279,7 +279,9 @@ impl Perform for Oper<Login> { } // Return the jwt - Ok(LoginResponse { jwt: user.jwt() }) + Ok(LoginResponse { + jwt: Claims::jwt(user, Settings::get().hostname), + }) } } @@ -421,7 +423,7 @@ impl Perform for Oper<Register> { // Return the jwt Ok(LoginResponse { - jwt: inserted_user.jwt(), + jwt: Claims::jwt(inserted_user, Settings::get().hostname), }) } } @@ -451,6 +453,11 @@ impl Perform for Oper<SaveUserSettings> { None => read_user.email, }; + let avatar = match &data.avatar { + Some(avatar) => Some(avatar.to_owned()), + None => read_user.avatar, + }; + let password_encrypted = match &data.new_password { Some(new_password) => { match &data.new_password_verify { @@ -488,7 +495,7 @@ impl Perform for Oper<SaveUserSettings> { name: read_user.name, email, matrix_user_id: data.matrix_user_id.to_owned(), - avatar: data.avatar.to_owned(), + avatar, password_encrypted, preferred_username: read_user.preferred_username, updated: Some(naive_now()), @@ -527,7 +534,7 @@ impl Perform for Oper<SaveUserSettings> { // Return the jwt Ok(LoginResponse { - jwt: updated_user.jwt(), + jwt: Claims::jwt(updated_user, Settings::get().hostname), }) } } @@ -678,7 +685,8 @@ impl Perform for Oper<AddAdmin> { } let added = data.added; - let add_admin = move |conn: &'_ _| User_::add_admin(conn, user_id, added); + let added_user_id = data.user_id; + let add_admin = move |conn: &'_ _| User_::add_admin(conn, added_user_id, added); if blocking(pool, add_admin).await?.is_err() { return Err(APIError::err("couldnt_update_user").into()); } @@ -1149,7 +1157,7 @@ impl Perform for Oper<PasswordChange> { // Return the jwt Ok(LoginResponse { - jwt: updated_user.jwt(), + jwt: Claims::jwt(updated_user, Settings::get().hostname), }) } } @@ -1207,7 +1215,12 @@ impl Perform for Oper<CreatePrivateMessage> { let inserted_private_message_id = inserted_private_message.id; let updated_private_message = match blocking(pool, move |conn| { - PrivateMessage::update_ap_id(&conn, inserted_private_message_id) + let apub_id = make_apub_endpoint( + EndpointType::PrivateMessage, + &inserted_private_message_id.to_string(), + ) + .to_string(); + PrivateMessage::update_ap_id(&conn, inserted_private_message_id, apub_id) }) .await? { diff --git a/server/src/apub/activities.rs b/server/src/apub/activities.rs index e5dc7045..204a380d 100644 --- a/server/src/apub/activities.rs +++ b/server/src/apub/activities.rs @@ -1,12 +1,18 @@ use crate::{ - apub::{extensions::signatures::sign, is_apub_id_valid, ActorType}, - db::{activity::insert_activity, community::Community, user::User_}, + apub::{ + community::do_announce, + extensions::signatures::sign, + insert_activity, + is_apub_id_valid, + ActorType, + }, request::retry_custom, DbPool, LemmyError, }; use activitystreams::{context, object::properties::ObjectProperties, public, Activity, Base}; use actix_web::client::Client; +use lemmy_db::{community::Community, user::User_}; use log::debug; use serde::Serialize; use std::fmt::Debug; @@ -43,7 +49,7 @@ where // if this is a local community, we need to do an announce from the community instead if community.local { - Community::do_announce(activity, &community, creator, client, pool).await?; + do_announce(activity, &community, creator, client, pool).await?; } else { send_activity(client, &activity, creator, to).await?; } diff --git a/server/src/apub/comment.rs b/server/src/apub/comment.rs index a42a52c2..af3581cb 100644 --- a/server/src/apub/comment.rs +++ b/server/src/apub/comment.rs @@ -17,19 +17,9 @@ use crate::{ ToApub, }, blocking, - convert_datetime, - db::{ - comment::{Comment, CommentForm}, - community::Community, - post::Post, - user::User_, - Crud, - }, routes::DbPoolParam, - scrape_text_for_mentions, DbPool, LemmyError, - MentionData, }; use activitystreams::{ activity::{Create, Delete, Dislike, Like, Remove, Undo, Update}, @@ -40,6 +30,14 @@ use activitystreams::{ use activitystreams_new::object::Tombstone; use actix_web::{body::Body, client::Client, web::Path, HttpResponse}; use itertools::Itertools; +use lemmy_db::{ + comment::{Comment, CommentForm}, + community::Community, + post::Post, + user::User_, + Crud, +}; +use lemmy_utils::{convert_datetime, scrape_text_for_mentions, MentionData}; use log::debug; use serde::Deserialize; @@ -123,7 +121,7 @@ impl FromApub for CommentForm { /// Parse an ActivityPub note received from another instance into a Lemmy comment async fn from_apub( - note: &Note, + note: &mut Note, client: &Client, pool: &DbPool, ) -> Result<CommentForm, LemmyError> { diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index f866511c..8b623e71 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -4,44 +4,48 @@ use crate::{ create_apub_response, create_apub_tombstone_response, create_tombstone, - extensions::{group_extensions::GroupExtension, signatures::PublicKey}, + extensions::group_extensions::GroupExtension, fetcher::get_or_fetch_and_upsert_remote_user, get_shared_inbox, + insert_activity, ActorType, FromApub, GroupExt, ToApub, }, blocking, - convert_datetime, - db::{ - activity::insert_activity, - community::{Community, CommunityForm}, - community_view::{CommunityFollowerView, CommunityModeratorView}, - user::User_, - }, - naive_now, routes::DbPoolParam, DbPool, LemmyError, }; use activitystreams::{ activity::{Accept, Announce, Delete, Remove, Undo}, - actor::{kind::GroupType, properties::ApActorProperties, Group}, - collection::UnorderedCollection, - context, - endpoint::EndpointProperties, - object::properties::ObjectProperties, Activity, Base, BaseBox, }; -use activitystreams_ext::Ext3; -use activitystreams_new::{activity::Follow, object::Tombstone}; +use activitystreams_ext::Ext2; +use activitystreams_new::{ + activity::Follow, + actor::{kind::GroupType, ApActor, Endpoints, Group}, + base::BaseExt, + collection::UnorderedCollection, + context, + object::Tombstone, + prelude::*, + primitives::{XsdAnyUri, XsdDateTime}, +}; use actix_web::{body::Body, client::Client, web, HttpResponse}; use itertools::Itertools; +use lemmy_db::{ + community::{Community, CommunityForm}, + community_view::{CommunityFollowerView, CommunityModeratorView}, + naive_now, + user::User_, +}; +use lemmy_utils::convert_datetime; use serde::{Deserialize, Serialize}; -use std::fmt::Debug; +use std::{fmt::Debug, str::FromStr}; #[derive(Deserialize)] pub struct CommunityQuery { @@ -54,9 +58,6 @@ impl ToApub for Community { // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network. async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> { - let mut group = Group::default(); - let oprops: &mut ObjectProperties = group.as_mut(); - // The attributed to, is an ordered vector with the creator actor_ids first, // then the rest of the moderators // TODO Technically the instance admins can mod the community, but lets @@ -66,36 +67,36 @@ impl ToApub for Community { CommunityModeratorView::for_community(&conn, id) }) .await??; - let moderators = moderators.into_iter().map(|m| m.user_actor_id).collect(); + let moderators: Vec<String> = moderators.into_iter().map(|m| m.user_actor_id).collect(); - oprops - .set_context_xsd_any_uri(context())? - .set_id(self.actor_id.to_owned())? - .set_name_xsd_string(self.name.to_owned())? - .set_published(convert_datetime(self.published))? - .set_many_attributed_to_xsd_any_uris(moderators)?; + let mut group = Group::new(); + group + .set_context(context()) + .set_id(XsdAnyUri::from_str(&self.actor_id)?) + .set_name(self.name.to_owned()) + .set_published(XsdDateTime::from(convert_datetime(self.published))) + .set_many_attributed_tos(moderators); if let Some(u) = self.updated.to_owned() |