Browse Source

Re-organizing federation tests. #746 #1040 (#1047)

* Re-organizing federation tests. #746 #1040

* Add more checks in inbox, plus some refactoring (#76)

Merge branch 'main' into more-inbox-permissions

Move check_community_ban() into helper function

Move slur check into helper functions

Move Claims::decode and site ban check into helper function

Note: this changes behaviour in that site ban is checked in more
places now. we could easily add a boolean parameter
check_for_site_ban to get the previous behaviour back

Rewrite user_inbox and community_inbox in the same way as shared_inbox

Add check against instance allowlist etc in shared_inbox

Co-authored-by: dessalines <dessalines@noreply.yerbamate.dev>
Co-authored-by: Felix Ableitner <me@nutomic.com>
Reviewed-on: https://yerbamate.dev/LemmyNet/lemmy/pulls/76

* Adding verbose to test results.

Co-authored-by: nutomic <nutomic@noreply.yerbamate.dev>
Co-authored-by: dessalines <dessalines@noreply.yerbamate.dev>
Co-authored-by: Felix Ableitner <me@nutomic.com>
federation-authorisation
Dessalines 1 year ago
committed by GitHub
parent
commit
e336e5bcc0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      docker/federation-test/servers.sh
  2. 6
      docker/federation/docker-compose.yml
  3. 6
      docker/travis/docker-compose.yml
  4. 10
      server/lemmy_db/src/comment.rs
  5. 10
      server/lemmy_db/src/community.rs
  6. 10
      server/lemmy_db/src/post.rs
  7. 194
      server/src/api/comment.rs
  8. 220
      server/src/api/community.rs
  9. 56
      server/src/api/mod.rs
  10. 268
      server/src/api/post.rs
  11. 121
      server/src/api/site.rs
  12. 209
      server/src/api/user.rs
  13. 7
      server/src/apub/activities.rs
  14. 6
      server/src/apub/fetcher.rs
  15. 1
      server/src/apub/inbox/activities/create.rs
  16. 60
      server/src/apub/inbox/community_inbox.rs
  17. 21
      server/src/apub/inbox/shared_inbox.rs
  18. 120
      server/src/apub/inbox/user_inbox.rs
  19. 23
      server/src/apub/mod.rs
  20. 2
      ui/package.json
  21. 1570
      ui/src/api_tests/api.spec.ts
  22. 308
      ui/src/api_tests/comment.spec.ts
  23. 88
      ui/src/api_tests/community.spec.ts
  24. 40
      ui/src/api_tests/follow.spec.ts
  25. 192
      ui/src/api_tests/post.spec.ts
  26. 71
      ui/src/api_tests/private_message.spec.ts
  27. 675
      ui/src/api_tests/shared.ts

1
docker/federation-test/servers.sh

@ -1,6 +1,7 @@
#!/bin/bash
set -e
sudo docker-compose --file ../federation/docker-compose.yml --project-directory . down
sudo rm -rf volumes
pushd ../../server/

6
docker/federation/docker-compose.yml

@ -39,6 +39,8 @@ services:
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
@ -66,6 +68,8 @@ services:
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-beta
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
@ -93,6 +97,8 @@ services:
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:

6
docker/travis/docker-compose.yml

@ -39,6 +39,8 @@ services:
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-alpha
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
@ -66,6 +68,8 @@ services:
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-beta
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:
@ -93,6 +97,8 @@ services:
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_gamma
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
- LEMMY_SETUP__SITE_NAME=lemmy-gamma
- LEMMY_RATE_LIMIT__POST=99999
- LEMMY_RATE_LIMIT__REGISTER=99999
- RUST_BACKTRACE=1
- RUST_LOG=debug
depends_on:

10
server/lemmy_db/src/comment.rs

@ -116,7 +116,10 @@ impl Comment {
) -> Result<Self, Error> {
use crate::schema::comment::dsl::*;
diesel::update(comment.find(comment_id))
.set(deleted.eq(new_deleted))
.set((
deleted.eq(new_deleted),
updated.eq(naive_now())
))
.get_result::<Self>(conn)
}
@ -127,7 +130,10 @@ impl Comment {
) -> Result<Self, Error> {
use crate::schema::comment::dsl::*;
diesel::update(comment.find(comment_id))
.set(removed.eq(new_removed))
.set((
removed.eq(new_removed),
updated.eq(naive_now())
))
.get_result::<Self>(conn)
}

10
server/lemmy_db/src/community.rs

@ -107,7 +107,10 @@ impl Community {
) -> Result<Self, Error> {
use crate::schema::community::dsl::*;
diesel::update(community.find(community_id))
.set(deleted.eq(new_deleted))
.set((
deleted.eq(new_deleted),
updated.eq(naive_now())
))
.get_result::<Self>(conn)
}
@ -118,7 +121,10 @@ impl Community {
) -> Result<Self, Error> {
use crate::schema::community::dsl::*;
diesel::update(community.find(community_id))
.set(removed.eq(new_removed))
.set((
removed.eq(new_removed),
updated.eq(naive_now())
))
.get_result::<Self>(conn)
}

10
server/lemmy_db/src/post.rs

@ -119,7 +119,10 @@ impl Post {
) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
diesel::update(post.find(post_id))
.set(deleted.eq(new_deleted))
.set((
deleted.eq(new_deleted),
updated.eq(naive_now())
))
.get_result::<Self>(conn)
}
@ -130,7 +133,10 @@ impl Post {
) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
diesel::update(post.find(post_id))
.set(removed.eq(new_removed))
.set((
removed.eq(new_removed),
updated.eq(naive_now())
))
.get_result::<Self>(conn)
}

194
server/src/api/comment.rs

@ -1,5 +1,13 @@
use crate::{
api::{claims::Claims, is_mod_or_admin, APIError, Oper, Perform},
api::{
check_community_ban,
get_user_from_jwt,
get_user_from_jwt_opt,
is_mod_or_admin,
APIError,
Oper,
Perform,
},
apub::{ApubLikeableType, ApubObjectType},
blocking,
websocket::{
@ -13,7 +21,6 @@ use crate::{
use lemmy_db::{
comment::*,
comment_view::*,
community_view::*,
moderator::*,
post::*,
site_view::*,
@ -123,13 +130,7 @@ impl Perform for Oper<CreateComment> {
websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &CreateComment = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let content_slurs_removed = remove_slurs(&data.content.to_owned());
@ -137,7 +138,7 @@ impl Perform for Oper<CreateComment> {
content: content_slurs_removed,
parent_id: data.parent_id.to_owned(),
post_id: data.post_id,
creator_id: user_id,
creator_id: user.id,
removed: None,
deleted: None,
read: None,
@ -151,18 +152,7 @@ impl Perform for Oper<CreateComment> {
let post_id = data.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let community_id = post.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(&conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
check_community_ban(user.id, post.community_id, pool).await?;
// Check if post is locked, no new comments
if post.locked {
@ -203,7 +193,7 @@ impl Perform for Oper<CreateComment> {
let like_form = CommentLikeForm {
comment_id: inserted_comment.id,
post_id: data.post_id,
user_id,
user_id: user.id,
score: 1,
};
@ -214,6 +204,7 @@ impl Perform for Oper<CreateComment> {
updated_comment.send_like(&user, &self.client, pool).await?;
let user_id = user.id;
let comment_view = blocking(pool, move |conn| {
CommentView::read(&conn, inserted_comment.id, Some(user_id))
})
@ -251,34 +242,16 @@ impl Perform for Oper<EditComment> {
websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &EditComment = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let edit_id = data.edit_id;
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Check for a community ban
let community_id = orig_comment.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
check_community_ban(user.id, orig_comment.community_id, pool).await?;
// Verify that only the creator can edit
if user_id != orig_comment.creator_id {
if user.id != orig_comment.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
@ -309,6 +282,7 @@ impl Perform for Oper<EditComment> {
send_local_notifs(mentions, updated_comment, &user, post, pool, false).await?;
let edit_id = data.edit_id;
let user_id = user.id;
let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, edit_id, Some(user_id))
})
@ -346,34 +320,16 @@ impl Perform for Oper<DeleteComment> {
websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &DeleteComment = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let edit_id = data.edit_id;
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Check for a community ban
let community_id = orig_comment.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
check_community_ban(user.id, orig_comment.community_id, pool).await?;
// Verify that only the creator can delete
if user_id != orig_comment.creator_id {
if user.id != orig_comment.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
@ -401,6 +357,7 @@ impl Perform for Oper<DeleteComment> {
// Refetch it
let edit_id = data.edit_id;
let user_id = user.id;
let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, edit_id, Some(user_id))
})
@ -445,34 +402,16 @@ impl Perform for Oper<RemoveComment> {
websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &RemoveComment = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let edit_id = data.edit_id;
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Check for a community ban
let community_id = orig_comment.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
check_community_ban(user.id, orig_comment.community_id, pool).await?;
// Verify that only a mod or admin can remove
is_mod_or_admin(pool, user_id, community_id).await?;
is_mod_or_admin(pool, user.id, orig_comment.community_id).await?;
// Do the remove
let removed = data.removed;
@ -487,7 +426,7 @@ impl Perform for Oper<RemoveComment> {
// Mod tables
let form = ModRemoveCommentForm {
mod_user_id: user_id,
mod_user_id: user.id,
comment_id: data.edit_id,
removed: Some(removed),
reason: data.reason.to_owned(),
@ -507,6 +446,7 @@ impl Perform for Oper<RemoveComment> {
// Refetch it
let edit_id = data.edit_id;
let user_id = user.id;
let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, edit_id, Some(user_id))
})
@ -551,31 +491,13 @@ impl Perform for Oper<MarkCommentAsRead> {
_websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &MarkCommentAsRead = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let edit_id = data.edit_id;
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, edit_id, None)).await??;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
// Check for a community ban
let community_id = orig_comment.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
check_community_ban(user.id, orig_comment.community_id, pool).await?;
// Verify that only the recipient can mark as read
// Needs to fetch the parent comment / post to get the recipient
@ -584,14 +506,14 @@ impl Perform for Oper<MarkCommentAsRead> {
Some(pid) => {
let parent_comment =
blocking(pool, move |conn| CommentView::read(&conn, pid, None)).await??;
if user_id != parent_comment.creator_id {
if user.id != parent_comment.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
}
None => {
let parent_post_id = orig_comment.post_id;
let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
if user_id != parent_post.creator_id {
if user.id != parent_post.creator_id {
return Err(APIError::err("no_comment_edit_allowed").into());
}
}
@ -606,6 +528,7 @@ impl Perform for Oper<MarkCommentAsRead> {
// Refetch it
let edit_id = data.edit_id;
let user_id = user.id;
let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, edit_id, Some(user_id))
})
@ -631,17 +554,11 @@ impl Perform for Oper<SaveComment> {
_websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &SaveComment = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let comment_saved_form = CommentSavedForm {
comment_id: data.comment_id,
user_id,
user_id: user.id,
};
if data.save {
@ -657,6 +574,7 @@ impl Perform for Oper<SaveComment> {
}
let comment_id = data.comment_id;
let user_id = user.id;
let comment_view = blocking(pool, move |conn| {
CommentView::read(conn, comment_id, Some(user_id))
})
@ -680,13 +598,7 @@ impl Perform for Oper<CreateCommentLike> {
websocket_info: Option<WebsocketInfo>,
) -> Result<CommentResponse, LemmyError> {
let data: &CreateCommentLike = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let mut recipient_ids = Vec::new();
@ -702,21 +614,9 @@ impl Perform for Oper<CreateCommentLike> {
let orig_comment =
blocking(pool, move |conn| CommentView::read(&conn, comment_id, None)).await??;
// Check for a community ban
let post_id = orig_comment.post_id;
let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
let community_id = post.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
check_community_ban(user.id, post.community_id, pool).await?;
let comment_id = data.comment_id;
let comment = blocking(pool, move |conn| Comment::read(conn, comment_id)).await??;
@ -725,7 +625,7 @@ impl Perform for Oper<CreateCommentLike> {
match comment.parent_id {
Some(parent_id) => {
let parent_comment = blocking(pool, move |conn| Comment::read(conn, parent_id)).await??;
if parent_comment.creator_id != user_id {
if parent_comment.creator_id != user.id {
let parent_user = blocking(pool, move |conn| {
User_::read(conn, parent_comment.creator_id)
})
@ -741,7 +641,7 @@ impl Perform for Oper<CreateCommentLike> {
let like_form = CommentLikeForm {
comment_id: data.comment_id,
post_id,
user_id,
user_id: user.id,
score: data.score,
};
@ -769,6 +669,7 @@ impl Perform for Oper<CreateCommentLike> {
// Have to refetch the comment to get the current state
let comment_id = data.comment_id;
let user_id = user.id;
let liked_comment = blocking(pool, move |conn| {
CommentView::read(conn, comment_id, Some(user_id))
})
@ -806,19 +707,8 @@ impl Perform for Oper<GetComments> {
websocket_info: Option<WebsocketInfo>,
) -> Result<GetCommentsResponse, LemmyError> {
let data: &GetComments = &self.data;
let user_claims: Option<Claims> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Ok(claims) => Some(claims.claims),
Err(_e) => None,
},
None => None,
};
let user_id = match &user_claims {
Some(claims) => Some(claims.id),
None => None,
};
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
let user_id = user.map(|u| u.id);
let type_ = ListingType::from_str(&data.type_)?;
let sort = SortType::from_str(&data.sort)?;

220
server/src/api/community.rs

@ -1,6 +1,6 @@
use super::*;
use crate::{
api::{claims::Claims, is_admin, is_mod_or_admin, APIError, Oper, Perform},
api::{is_admin, is_mod_or_admin, APIError, Oper, Perform},
apub::ActorType,
blocking,
websocket::{
@ -16,8 +16,6 @@ use lemmy_utils::{
is_valid_community_name,
make_apub_endpoint,
naive_from_unix,
slur_check,
slurs_vec_to_str,
EndpointType,
};
use serde::{Deserialize, Serialize};
@ -154,17 +152,8 @@ impl Perform for Oper<GetCommunity> {
websocket_info: Option<WebsocketInfo>,
) -> Result<GetCommunityResponse, LemmyError> {
let data: &GetCommunity = &self.data;
let user_id: Option<i32> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Ok(claims) => {
let user_id = claims.claims.id;
Some(user_id)
}
Err(_e) => None,
},
None => None,
};
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
let user_id = user.map(|u| u.id);
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
let community = match data.id {
@ -234,38 +223,16 @@ impl Perform for Oper<CreateCommunity> {
_websocket_info: Option<WebsocketInfo>,
) -> Result<CommunityResponse, LemmyError> {
let data: &CreateCommunity = &self.data;
let user = get_user_from_jwt(&data.auth, pool).await?;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
if let Err(slurs) = slur_check(&data.name) {
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
}
if let Err(slurs) = slur_check(&data.title) {
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
}
if let Some(description) = &data.description {
if let Err(slurs) = slur_check(description) {
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
}
}
check_slurs(&data.name)?;
check_slurs(&data.title)?;
check_slurs_opt(&data.description)?;
if !is_valid_community_name(&data.name) {
return Err(APIError::err("invalid_community_name").into());
}
let user_id = claims.id;
// Check for a site ban
let user_view = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
if user_view.banned {
return Err(APIError::err("site_ban").into());
}
// Double check for duplicate community actor_ids
let actor_id = make_apub_endpoint(EndpointType::Community, &data.name).to_string();
let actor_id_cloned = actor_id.to_owned();
@ -285,7 +252,7 @@ impl Perform for Oper<CreateCommunity> {
title: data.title.to_owned(),
description: data.description.to_owned(),
category_id: data.category_id,
creator_id: user_id,
creator_id: user.id,
removed: None,
deleted: None,
nsfw: data.nsfw,
@ -306,7 +273,7 @@ impl Perform for Oper<CreateCommunity> {
let community_moderator_form = CommunityModeratorForm {
community_id: inserted_community.id,
user_id,
user_id: user.id,
};
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@ -316,7 +283,7 @@ impl Perform for Oper<CreateCommunity> {
let community_follower_form = CommunityFollowerForm {
community_id: inserted_community.id,
user_id,
user_id: user.id,
};
let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
@ -324,6 +291,7 @@ impl Perform for Oper<CreateCommunity> {
return Err(APIError::err("community_follower_already_exists").into());
}
let user_id = user.id;
let community_view = blocking(pool, move |conn| {
CommunityView::read(conn, inserted_community.id, Some(user_id))
})
@ -345,29 +313,10 @@ impl Perform for Oper<EditCommunity> {
websocket_info: Option<WebsocketInfo>,
) -> Result<CommunityResponse, LemmyError> {
let data: &EditCommunity = &self.data;
let user = get_user_from_jwt(&data.auth, pool).await?;
if let Err(slurs) = slur_check(&data.title) {
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
}
if let Some(description) = &data.description {
if let Err(slurs) = slur_check(description) {
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
}
}
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
check_slurs(&data.title)?;
check_slurs_opt(&data.description)?;
// Verify its a mod (only mods can edit it)
let edit_id = data.edit_id;
@ -376,7 +325,7 @@ impl Perform for Oper<EditCommunity> {
.map(|v| v.into_iter().map(|m| m.user_id).collect())
})
.await??;
if !mods.contains(&user_id) {
if !mods.contains(&user.id) {
return Err(APIError::err("not_a_moderator").into());
}
@ -415,6 +364,7 @@ impl Perform for Oper<EditCommunity> {
// process for communities and users
let edit_id = data.edit_id;
let user_id = user.id;
let community_view = blocking(pool, move |conn| {
CommunityView::read(conn, edit_id, Some(user_id))
})
@ -440,24 +390,12 @@ impl Perform for Oper<DeleteCommunity> {
websocket_info: Option<WebsocketInfo>,
) -> Result<CommunityResponse, LemmyError> {
let data: &DeleteCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
let user = get_user_from_jwt(&data.auth, pool).await?;
// Verify its the creator (only a creator can delete the community)
let edit_id = data.edit_id;
let read_community = blocking(pool, move |conn| Community::read(conn, edit_id)).await??;
if read_community.creator_id != user_id {
if read_community.creator_id != user.id {
return Err(APIError::err("no_community_edit_allowed").into());
}
@ -485,6 +423,7 @@ impl Perform for Oper<DeleteCommunity> {
}
let edit_id = data.edit_id;
let user_id = user.id;
let community_view = blocking(pool, move |conn| {
CommunityView::read(conn, edit_id, Some(user_id))
})
@ -510,22 +449,10 @@ impl Perform for Oper<RemoveCommunity> {
websocket_info: Option<WebsocketInfo>,
) -> Result<CommunityResponse, LemmyError> {
let data: &RemoveCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
let user = get_user_from_jwt(&data.auth, pool).await?;
// Verify its an admin (only an admin can remove a community)
is_admin(pool, user_id).await?;
is_admin(pool, user.id).await?;
// Do the remove
let edit_id = data.edit_id;
@ -545,7 +472,7 @@ impl Perform for Oper<RemoveCommunity> {
None => None,
};
let form = ModRemoveCommunityForm {
mod_user_id: user_id,
mod_user_id: user.id,
community_id: data.edit_id,
removed: Some(removed),
reason: data.reason.to_owned(),
@ -565,6 +492,7 @@ impl Perform for Oper<RemoveCommunity> {
}
let edit_id = data.edit_id;
let user_id = user.id;
let community_view = blocking(pool, move |conn| {
CommunityView::read(conn, edit_id, Some(user_id))
})
@ -590,19 +518,7 @@ impl Perform for Oper<ListCommunities> {
_websocket_info: Option<WebsocketInfo>,
) -> Result<ListCommunitiesResponse, LemmyError> {
let data: &ListCommunities = &self.data;
// For logged in users, you need to get back subscribed, and settings
let user: Option<User_> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Ok(claims) => {
let user_id = claims.claims.id;
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
Some(user)
}
Err(_e) => None,
},
None => None,
};
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
let user_id = match &user {
Some(user) => Some(user.id),
@ -644,19 +560,13 @@ impl Perform for Oper<FollowCommunity> {
_websocket_info: Option<WebsocketInfo>,
) -> Result<CommunityResponse, LemmyError> {
let data: &FollowCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let community_id = data.community_id;
let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
let community_follower_form = CommunityFollowerForm {
community_id: data.community_id,
user_id,
user_id: user.id,
};
if community.local {
@ -672,29 +582,25 @@ impl Perform for Oper<FollowCommunity> {
return Err(APIError::err("community_follower_already_exists").into());
}
}
} else if data.follow {
// 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)
.await?;
} else {
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if data.follow {
// 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)
.await?;
} else {
user
.send_unfollow(&community.actor_id, &self.client, pool)
.await?;
let unfollow =
move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
if blocking(pool, unfollow).await?.is_err() {
return Err(APIError::err("community_follower_already_exists").into());
}
user
.send_unfollow(&community.actor_id, &self.client, pool)
.await?;
let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
if blocking(pool, unfollow).await?.is_err() {
return Err(APIError::err("community_follower_already_exists").into());
}
// TODO: this needs to return a "pending" state, until Accept is received from the remote server
}
// TODO: this needs to return a "pending" state, until Accept is received from the remote server
let community_id = data.community_id;
let user_id = user.id;
let community_view = blocking(pool, move |conn| {
CommunityView::read(conn, community_id, Some(user_id))
})
@ -716,14 +622,9 @@ impl Perform for Oper<GetFollowedCommunities> {
_websocket_info: Option<WebsocketInfo>,
) -> Result<GetFollowedCommunitiesResponse, LemmyError> {
let data: &GetFollowedCommunities = &self.data;
let user = get_user_from_jwt(&data.auth, pool).await?;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user_id = user.id;
let communities = match blocking(pool, move |conn| {
CommunityFollowerView::for_user(conn, user_id)
})
@ -748,18 +649,12 @@ impl Perform for Oper<BanFromCommunity> {
websocket_info: Option<WebsocketInfo>,
) -> Result<BanFromCommunityResponse, LemmyError> {
let data: &BanFromCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let community_id = data.community_id;
// Verify that only mods or admins can ban
is_mod_or_admin(pool, user_id, community_id).await?;
is_mod_or_admin(pool, user.id, community_id).await?;
let community_user_ban_form = CommunityUserBanForm {
community_id: data.community_id,
@ -786,7 +681,7 @@ impl Perform for Oper<BanFromCommunity> {
};
let form = ModBanFromCommunityForm {
mod_user_id: user_id,
mod_user_id: user.id,
other_user_id: data.user_id,
community_id: data.community_id,
reason: data.reason.to_owned(),
@ -826,13 +721,7 @@ impl Perform for Oper<AddModToCommunity> {
websocket_info: Option<WebsocketInfo>,
) -> Result<AddModToCommunityResponse, LemmyError> {
let data: &AddModToCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let community_moderator_form = CommunityModeratorForm {
community_id: data.community_id,
@ -842,7 +731,7 @@ impl Perform for Oper<AddModToCommunity> {
let community_id = data.community_id;
// Verify that only mods or admins can add mod
is_mod_or_admin(pool, user_id, community_id).await?;
is_mod_or_admin(pool, user.id, community_id).await?;
if data.added {
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@ -858,7 +747,7 @@ impl Perform for Oper<AddModToCommunity> {
// Mod tables
let form = ModAddCommunityForm {
mod_user_id: user_id,
mod_user_id: user.id,
other_user_id: data.user_id,
community_id: data.community_id,
removed: Some(!data.added),
@ -896,13 +785,7 @@ impl Perform for Oper<TransferCommunity> {
_websocket_info: Option<WebsocketInfo>,
) -> Result<GetCommunityResponse, LemmyError> {
let data: &TransferCommunity = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = get_user_from_jwt(&data.auth, pool).await?;
let community_id = data.community_id;
let read_community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
@ -917,7 +800,7 @@ impl Perform for Oper<TransferCommunity> {
admins.insert(0, creator_user);
// Make sure user is the creator, or an admin
if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
if user.id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user.id) {
return Err(APIError::err("not_an_admin").into());
}
@ -962,7 +845,7 @@ impl Perform for Oper<TransferCommunity> {
// Mod tables
let form = ModAddCommunityForm {
mod_user_id: user_id,
mod_user_id: user.id,
other_user_id: data.user_id,
community_id: data.community_id,
removed: Some(false),
@ -970,6 +853,7 @@ impl Perform for Oper<TransferCommunity> {
blocking(pool, move |conn| ModAddCommunity::create(conn, &form)).await??;
let community_id = data.community_id;
let user_id = user.id;
let community_view = match blocking(pool, move |conn| {
CommunityView::read(conn, community_id, Some(user_id))
})

56
server/src/api/mod.rs

@ -1,4 +1,4 @@
use crate::{blocking, websocket::WebsocketInfo, DbPool, LemmyError};
use crate::{api::claims::Claims, blocking, websocket::WebsocketInfo, DbPool, LemmyError};
use actix_web::client::Client;
use lemmy_db::{
community::*,
@ -9,6 +9,7 @@ use lemmy_db::{
user_view::*,
Crud,
};
use lemmy_utils::{slur_check, slurs_vec_to_str};
use thiserror::Error;
pub mod claims;
@ -75,3 +76,56 @@ pub async fn is_admin(pool: &DbPool, user_id: i32) -> Result<(), LemmyError> {
}
Ok(())
}
pub(in crate::api) async fn get_user_from_jwt(
jwt: &str,
pool: &DbPool,
) -> Result<User_, LemmyError> {
let claims = match Claims::decode(&jwt) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id = claims.id;
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
// Check for a site ban
if user.banned {
return Err(APIError::err("site_ban").into());
}
Ok(user)
}
pub(in crate::api) async fn get_user_from_jwt_opt(
jwt: &Option<String>,
pool: &DbPool,
) -> Result<Option<User_>, LemmyError> {
match jwt {
Some(jwt) => Ok(Some(get_user_from_jwt(jwt, pool).await?)),
None => Ok(None),
}
}
pub(in crate::api) fn check_slurs(text: &str) -> Result<(), APIError> {
if let Err(slurs) = slur_check(text) {
Err(APIError::err(&slurs_vec_to_str(slurs)))
} else {
Ok(())
}
}
pub(in crate::api) fn check_slurs_opt(text: &Option<String>) -> Result<(), APIError> {
match text {
Some(t) => check_slurs(t),
None => Ok(()),
}
}
pub(in crate::api) async fn check_community_ban(
user_id: i32,
community_id: i32,
pool: &DbPool,
) -> Result<(), LemmyError> {
let is_banned = move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
Err(APIError::err("community_ban").into())
} else {
Ok(())
}
}

268
server/src/api/post.rs

@ -1,5 +1,15 @@
use crate::{
api::{claims::Claims, is_mod_or_admin, APIError, Oper, Perform},
api::{
check_community_ban,
check_slurs,
check_slurs_opt,
get_user_from_jwt,
get_user_from_jwt_opt,
is_mod_or_admin,
APIError,
Oper,
Perform,
},
apub::{ApubLikeableType, ApubObjectType},
blocking,
fetch_iframely_and_pictrs_data,
@ -19,20 +29,13 @@ use lemmy_db::{
post::*,
post_view::*,
site_view::*,
user::*,
Crud,
Likeable,
ListingType,
Saveable,
SortType,
};
use lemmy_utils::{
is_valid_post_title,
make_apub_endpoint,
slur_check,
slurs_vec_to_str,
EndpointType,
};
use lemmy_utils::{is_valid_post_title, make_apub_endpoint, EndpointType};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use url::Url;
@ -146,41 +149,16 @@ impl Perform for Oper<CreatePost> {
websocket_info: Option<WebsocketInfo>,
) -> Result<PostResponse, LemmyError> {
let data: &CreatePost = &self.data;
let user = get_user_from_jwt(&data.auth, pool).await?;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
if let Err(slurs) = slur_check(&data.name) {
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
}
if let Some(body) = &data.body {
if let Err(slurs) = slur_check(body) {
return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
}
}
check_slurs(&data.name)?;
check_slurs_opt(&data.body)?;
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
let community_id = data.community_id;
let is_banned =
move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
if blocking(pool, is_banned).await? {
return Err(APIError::err("community_ban").into());
}
// Check for a site ban
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
if user.banned {
return Err(APIError::err("site_ban").into());
}
check_community_ban(user.id, data.community_id, pool).await?;
if let Some(url) = data.url.as_ref() {
match Url::parse(url) {
@ -198,7 +176,7 @@ impl Perform for Oper<CreatePost> {
url: data.url.to_owned(),
body: data.body.to_owned(),
community_id: data.community_id,
creator_id: user_id,
creator_id: user.id,
removed: None,
deleted: None,
nsfw: data.nsfw,
@ -244,7 +222,7 @@ impl Perform for Oper<CreatePost> {
// They like their own post by default
let like_form = PostLikeForm {
post_id: inserted_post.id,
user_id,
user_id: user.id,
score: 1,
};
@ -258,7 +236,7 @@ impl Perform for Oper<CreatePost> {
// Refetch the view
let inserted_post_id = inserted_post.id;
let post_view = match blocking(pool, move |conn| {
PostView::read(conn, inserted_post_id, Some(user_id))
PostView::read(conn, inserted_post_id, Some(user.id))
})
.await?
{
@ -290,17 +268,8 @@ impl Perform for Oper<GetPost> {
websocket_info: Option<WebsocketInfo>,
) -> Result<GetPostResponse, LemmyError> {
let data: &GetPost = &self.data;
let user_id: Option<i32> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Ok(claims) => {
let user_id = claims.claims.id;
Some(user_id)
}
Err(_e) => None,
},
None => None,
};
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
let user_id = user.map(|u| u.id);
let id = data.id;
let post_view = match blocking(pool, move |conn| PostView::read(conn, id, user_id)).await? {
@ -369,19 +338,7 @@ impl Perform for Oper<GetPosts> {
websocket_info: Option<WebsocketInfo>,
) -> Result<GetPostsResponse, LemmyError> {
let data: &GetPosts = &self.data;
// For logged in users, you need to get back subscribed, and settings
let user: Option<User_> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Ok(claims) => {
let user_id = claims.claims.id;
let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
Some(user)
}
Err(_e) => None,
},
None => None,
};
let user = get_user_from_jwt_opt(&data.auth, pool).await?;
let user_id = match &user {
Some(user) => Some(user.id),
@ -446,13 +403,7 @@ impl Perform for Oper<CreatePostLike> {
websocket_info: Option<WebsocketInfo>,
) -> Result<PostResponse, LemmyError> {
let data: &CreatePostLike = &self.data;
let claims = match Claims::decode(&data.auth) {
Ok(claims) => claims.claims,
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
let user_id =