You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lemmy/crates/apub_receive/src/activities/receive/community.rs

233 lines
6.6 KiB
Rust

use crate::{
activities::receive::get_actor_as_person,
inbox::receive_for_community::verify_actor_is_community_mod,
};
use activitystreams::{
activity::{ActorAndObjectRefExt, Delete, Undo, Update},
base::ExtendsExt,
};
use anyhow::{anyhow, Context};
use lemmy_api_common::{blocking, community::CommunityResponse};
use lemmy_apub::{
get_community_from_to_or_cc,
objects::FromApubToForm,
ActorType,
CommunityType,
GroupExt,
};
use lemmy_db_queries::{source::community::Community_, Crud};
use lemmy_db_schema::source::{
community::{Community, CommunityForm},
person::Person,
};
use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView,
community_view::CommunityView,
};
use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperationCrud};
/// This activity is received from a remote community mod, and updates the description or other
/// fields of a local community.
pub(crate) async fn receive_remote_mod_update_community(
update: Update,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let community = get_community_from_to_or_cc(&update, context, request_counter).await?;
verify_actor_is_community_mod(&update, &community, context).await?;
let group = GroupExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
let updated_community = CommunityForm::from_apub(
&group,
context,
community.actor_id(),
request_counter,
false,
)
.await?;
let cf = CommunityForm {
name: updated_community.name,
title: updated_community.title,
description: updated_community.description,
nsfw: updated_community.nsfw,
// TODO: icon and banner would be hosted on the other instance, ideally we would copy it to ours
icon: updated_community.icon,
banner: updated_community.banner,
..CommunityForm::default()
};
blocking(context.pool(), move |conn| {
Community::update(conn, community.id, &cf)
})
.await??;
Ok(())
}
pub(crate) async fn receive_remote_mod_delete_community(
delete: Delete,
community: Community,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_actor_is_community_mod(&delete, &community, context).await?;
let actor = get_actor_as_person(&delete, context, request_counter).await?;
verify_is_remote_community_creator(&actor, &community, context).await?;
let community_id = community.id;
blocking(context.pool(), move |conn| {
Community::update_deleted(conn, community_id, true)
})
.await??;
community.send_delete(actor, context).await
}
pub(crate) async fn receive_delete_community(
context: &LemmyContext,
community: Community,
) -> Result<(), LemmyError> {
let deleted_community = blocking(context.pool(), move |conn| {
Community::update_deleted(conn, community.id, true)
})
.await??;
let community_id = deleted_community.id;
let res = CommunityResponse {
community_view: blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, None)
})
.await??,
};
let community_id = res.community_view.community.id;
context.chat_server().do_send(SendCommunityRoomMessage {
op: UserOperationCrud::EditCommunity,
response: res,
community_id,
websocket_id: None,
});
Ok(())
}
pub(crate) async fn receive_remove_community(
context: &LemmyContext,
community: Community,
) -> Result<(), LemmyError> {
let removed_community = blocking(context.pool(), move |conn| {
Community::update_removed(conn, community.id, true)
})
.await??;
let community_id = removed_community.id;
let res = CommunityResponse {
community_view: blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, None)
})
.await??,
};
let community_id = res.community_view.community.id;
context.chat_server().do_send(SendCommunityRoomMessage {
op: UserOperationCrud::EditCommunity,
response: res,
community_id,
websocket_id: None,
});
Ok(())
}
pub(crate) async fn receive_remote_mod_undo_delete_community(
undo: Undo,
community: Community,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_actor_is_community_mod(&undo, &community, context).await?;
let actor = get_actor_as_person(&undo, context, request_counter).await?;
verify_is_remote_community_creator(&actor, &community, context).await?;
let community_id = community.id;
blocking(context.pool(), move |conn| {
Community::update_deleted(conn, community_id, false)
})
.await??;
community.send_undo_delete(actor, context).await
}
pub(crate) async fn receive_undo_delete_community(
context: &LemmyContext,
community: Community,
) -> Result<(), LemmyError> {
let deleted_community = blocking(context.pool(), move |conn| {
Community::update_deleted(conn, community.id, false)
})
.await??;
let community_id = deleted_community.id;
let res = CommunityResponse {
community_view: blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, None)
})
.await??,
};
let community_id = res.community_view.community.id;
context.chat_server().do_send(SendCommunityRoomMessage {
op: UserOperationCrud::EditCommunity,
response: res,
community_id,
websocket_id: None,
});
Ok(())
}
pub(crate) async fn receive_undo_remove_community(
context: &LemmyContext,
community: Community,
) -> Result<(), LemmyError> {
let removed_community = blocking(context.pool(), move |conn| {
Community::update_removed(conn, community.id, false)
})
.await??;
let community_id = removed_community.id;
let res = CommunityResponse {
community_view: blocking(context.pool(), move |conn| {
CommunityView::read(conn, community_id, None)
})
.await??,
};
let community_id = res.community_view.community.id;
context.chat_server().do_send(SendCommunityRoomMessage {
op: UserOperationCrud::EditCommunity,
response: res,
community_id,
websocket_id: None,
});
Ok(())
}
/// Checks if the remote user is creator of the local community. This can only happen if a community
/// is created by a local user, and then transferred to a remote user.
async fn verify_is_remote_community_creator(
user: &Person,
community: &Community,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let community_id = community.id;
let community_mods = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_community(conn, community_id)
})
.await??;
if user.id != community_mods[0].moderator.id {
Err(anyhow!("Actor is not community creator").into())
} else {
Ok(())
}
}