Membership refactor
Introduce deactivate, membership services Move membership and user deactivation functions from api crate into those services
This commit is contained in:
200
src/service/membership/invite.rs
Normal file
200
src/service/membership/invite.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
OwnedServerName, RoomId, UserId,
|
||||
api::federation::membership::create_invite,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Result, err, implement, matrix::event::gen_event_id_canonical_json, pdu::PduBuilder,
|
||||
};
|
||||
|
||||
use super::Service;
|
||||
|
||||
#[implement(Service)]
|
||||
#[tracing::instrument(
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%sender_user, %room_id, %user_id)
|
||||
)]
|
||||
pub async fn invite(
|
||||
&self,
|
||||
sender_user: &UserId,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<&String>,
|
||||
is_direct: bool,
|
||||
) -> Result {
|
||||
if self.services.globals.user_is_local(user_id) {
|
||||
self.local_invite(sender_user, user_id, room_id, reason, is_direct)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
self.remote_invite(sender_user, user_id, room_id, reason, is_direct)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[implement(Service)]
|
||||
#[tracing::instrument(name = "remote", level = "debug", skip_all)]
|
||||
async fn remote_invite(
|
||||
&self,
|
||||
sender_user: &UserId,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<&String>,
|
||||
is_direct: bool,
|
||||
) -> Result {
|
||||
let (pdu, pdu_json, invite_room_state) = {
|
||||
let state_lock = self.services.state.mutex.lock(room_id).await;
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
avatar_url: self.services.users.avatar_url(user_id).await.ok(),
|
||||
is_direct: Some(is_direct),
|
||||
reason: reason.cloned(),
|
||||
..RoomMemberEventContent::new(MembershipState::Invite)
|
||||
};
|
||||
|
||||
let (pdu, pdu_json) = self
|
||||
.services
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(user_id.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let invite_room_state = self.services.state.summary_stripped(&pdu).await;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
(pdu, pdu_json, invite_room_state)
|
||||
};
|
||||
|
||||
let room_version_id = self
|
||||
.services
|
||||
.state
|
||||
.get_room_version(room_id)
|
||||
.await?;
|
||||
|
||||
let response = self
|
||||
.services
|
||||
.sending
|
||||
.send_federation_request(user_id.server_name(), create_invite::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: (*pdu.event_id).to_owned(),
|
||||
room_version: room_version_id.clone(),
|
||||
event: self
|
||||
.services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(pdu_json.clone())
|
||||
.await,
|
||||
invite_room_state: invite_room_state
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
via: self
|
||||
.services
|
||||
.state_cache
|
||||
.servers_route_via(room_id)
|
||||
.await
|
||||
.ok(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
// We do not add the event_id field to the pdu here because of signature and
|
||||
// hashes checks
|
||||
let (event_id, value) = gen_event_id_canonical_json(&response.event, &room_version_id)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||
})?;
|
||||
|
||||
if pdu.event_id != event_id {
|
||||
return Err!(Request(BadJson(warn!(
|
||||
%pdu.event_id, %event_id,
|
||||
"Server {} sent event with wrong event ID",
|
||||
user_id.server_name()
|
||||
))));
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(serde_json::to_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event missing origin field."))))?,
|
||||
)?)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}"))))
|
||||
})?;
|
||||
|
||||
let pdu_id = self
|
||||
.services
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
err!(Request(InvalidParam("Could not accept incoming PDU as timeline event.")))
|
||||
})?;
|
||||
|
||||
self.services
|
||||
.sending
|
||||
.send_pdu_room(room_id, &pdu_id)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[implement(Service)]
|
||||
#[tracing::instrument(name = "local", level = "debug", skip_all)]
|
||||
async fn local_invite(
|
||||
&self,
|
||||
sender_user: &UserId,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<&String>,
|
||||
is_direct: bool,
|
||||
) -> Result {
|
||||
if !self
|
||||
.services
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden(
|
||||
"You must be joined in the room you are trying to invite from."
|
||||
)));
|
||||
}
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(room_id).await;
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: self
|
||||
.services
|
||||
.users
|
||||
.displayname(user_id)
|
||||
.await
|
||||
.ok(),
|
||||
avatar_url: self.services.users.avatar_url(user_id).await.ok(),
|
||||
blurhash: self.services.users.blurhash(user_id).await.ok(),
|
||||
is_direct: Some(is_direct),
|
||||
reason: reason.cloned(),
|
||||
..RoomMemberEventContent::new(MembershipState::Invite)
|
||||
};
|
||||
|
||||
self.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(user_id.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user