mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
refactor: Fix errors in api/client/state.rs
This commit is contained in:
+124
-117
@@ -5,13 +5,15 @@ use axum_client_ip::ClientIp;
|
|||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Result, err,
|
Err, Result, err,
|
||||||
matrix::{Event, pdu::PartialPdu},
|
matrix::{Event, pdu::PartialPdu},
|
||||||
utils::BoolExt,
|
|
||||||
};
|
};
|
||||||
use conduwuit_service::Services;
|
use conduwuit_service::Services;
|
||||||
use futures::{FutureExt, TryStreamExt};
|
use futures::{FutureExt, TryStreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, UserId,
|
MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, UserId,
|
||||||
api::client::state::{get_state_events, get_state_events_for_key, send_state_event},
|
api::client::state::{
|
||||||
|
get_state_event_for_key::{self, v3::StateEventFormat},
|
||||||
|
get_state_events, send_state_event,
|
||||||
|
},
|
||||||
events::{
|
events::{
|
||||||
AnyStateEventContent, StateEventType,
|
AnyStateEventContent, StateEventType,
|
||||||
room::{
|
room::{
|
||||||
@@ -24,7 +26,7 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::{json, value::to_raw_value};
|
||||||
|
|
||||||
use crate::{Ruma, RumaResponse};
|
use crate::{Ruma, RumaResponse};
|
||||||
|
|
||||||
@@ -46,23 +48,23 @@ pub(crate) async fn send_state_event_for_key_route(
|
|||||||
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(send_state_event::v3::Response {
|
let event_id = send_state_event_for_key_helper(
|
||||||
event_id: send_state_event_for_key_helper(
|
&services,
|
||||||
&services,
|
sender_user,
|
||||||
sender_user,
|
&body.room_id,
|
||||||
&body.room_id,
|
&body.event_type,
|
||||||
&body.event_type,
|
&body.body.body,
|
||||||
&body.body.body,
|
&body.state_key,
|
||||||
&body.state_key,
|
if body.appservice_info.is_some() {
|
||||||
if body.appservice_info.is_some() {
|
body.timestamp
|
||||||
body.timestamp
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
},
|
||||||
},
|
)
|
||||||
)
|
.boxed()
|
||||||
.boxed()
|
.await?;
|
||||||
.await?,
|
|
||||||
})
|
Ok(send_state_event::v3::Response::new(event_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}`
|
/// # `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}`
|
||||||
@@ -100,15 +102,15 @@ pub(crate) async fn get_state_events_route(
|
|||||||
return Err!(Request(Forbidden("You don't have permission to view the room state.")));
|
return Err!(Request(Forbidden("You don't have permission to view the room state.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(get_state_events::v3::Response {
|
let room_state = services
|
||||||
room_state: services
|
.rooms
|
||||||
.rooms
|
.state_accessor
|
||||||
.state_accessor
|
.room_state_full_pdus(&body.room_id)
|
||||||
.room_state_full_pdus(&body.room_id)
|
.map_ok(Event::into_format)
|
||||||
.map_ok(Event::into_format)
|
.try_collect()
|
||||||
.try_collect()
|
.await?;
|
||||||
.await?,
|
|
||||||
})
|
Ok(get_state_events::v3::Response::new(room_state))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v3/rooms/{roomid}/state/{eventType}/{stateKey}`
|
/// # `GET /_matrix/client/v3/rooms/{roomid}/state/{eventType}/{stateKey}`
|
||||||
@@ -119,10 +121,10 @@ pub(crate) async fn get_state_events_route(
|
|||||||
///
|
///
|
||||||
/// - If not joined: Only works if current room history visibility is world
|
/// - If not joined: Only works if current room history visibility is world
|
||||||
/// readable
|
/// readable
|
||||||
pub(crate) async fn get_state_events_for_key_route(
|
pub(crate) async fn get_state_event_for_key_route(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_state_events_for_key::v3::Request>,
|
body: Ruma<get_state_event_for_key::v3::Request>,
|
||||||
) -> Result<get_state_events_for_key::v3::Response> {
|
) -> Result<get_state_event_for_key::v3::Response> {
|
||||||
let sender_user = body.sender_user();
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
if !services
|
if !services
|
||||||
@@ -149,26 +151,23 @@ pub(crate) async fn get_state_events_for_key_route(
|
|||||||
))))
|
))))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let event_format = body
|
let content = match body.format {
|
||||||
.format
|
| StateEventFormat::Content => event.content,
|
||||||
.as_ref()
|
| StateEventFormat::Event => to_raw_value(&json!({
|
||||||
.is_some_and(|f| f.to_lowercase().eq("event"));
|
"content": event.content(),
|
||||||
|
"event_id": event.event_id(),
|
||||||
|
"origin_server_ts": event.origin_server_ts(),
|
||||||
|
"room_id": event.room_id_or_hash(),
|
||||||
|
"sender": event.sender(),
|
||||||
|
"state_key": event.state_key(),
|
||||||
|
"type": event.kind(),
|
||||||
|
"unsigned": event.unsigned(),
|
||||||
|
}))
|
||||||
|
.unwrap(),
|
||||||
|
| _ => return Err!(Request(InvalidParam("Unknown response format"))),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(get_state_events_for_key::v3::Response {
|
Ok(get_state_event_for_key::v3::Response::new(content))
|
||||||
content: event_format.or(|| event.get_content_as_value()),
|
|
||||||
event: event_format.then(|| {
|
|
||||||
json!({
|
|
||||||
"content": event.content(),
|
|
||||||
"event_id": event.event_id(),
|
|
||||||
"origin_server_ts": event.origin_server_ts(),
|
|
||||||
"room_id": event.room_id_or_hash(),
|
|
||||||
"sender": event.sender(),
|
|
||||||
"state_key": event.state_key(),
|
|
||||||
"type": event.kind(),
|
|
||||||
"unsigned": event.unsigned(),
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v3/rooms/{roomid}/state/{eventType}`
|
/// # `GET /_matrix/client/v3/rooms/{roomid}/state/{eventType}`
|
||||||
@@ -181,9 +180,9 @@ pub(crate) async fn get_state_events_for_key_route(
|
|||||||
/// readable
|
/// readable
|
||||||
pub(crate) async fn get_state_events_for_empty_key_route(
|
pub(crate) async fn get_state_events_for_empty_key_route(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<get_state_events_for_key::v3::Request>,
|
body: Ruma<get_state_event_for_key::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
) -> Result<RumaResponse<get_state_event_for_key::v3::Response>> {
|
||||||
get_state_events_for_key_route(State(services), body)
|
get_state_event_for_key_route(State(services), body)
|
||||||
.await
|
.await
|
||||||
.map(RumaResponse)
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
@@ -237,9 +236,16 @@ async fn allowed_to_send_state_event(
|
|||||||
| StateEventType::RoomServerAcl => {
|
| StateEventType::RoomServerAcl => {
|
||||||
// prevents common ACL paw-guns as ACL management is difficult and prone to
|
// prevents common ACL paw-guns as ACL management is difficult and prone to
|
||||||
// irreversible mistakes
|
// irreversible mistakes
|
||||||
match json.deserialize_as::<RoomServerAclEventContent>() {
|
match json.deserialize_as_unchecked::<RoomServerAclEventContent>() {
|
||||||
| Ok(acl_content) => {
|
| Ok(acl_content) => {
|
||||||
if acl_content.allow_is_empty() {
|
let allow_has_wildcard = acl_content.allow.iter().any(|entry| entry == "*");
|
||||||
|
let deny_has_wildcard = acl_content.deny.iter().any(|entry| entry == "*");
|
||||||
|
let allow_has_server = acl_content
|
||||||
|
.allow
|
||||||
|
.iter()
|
||||||
|
.any(|entry| entry == services.globals.server_name().as_str());
|
||||||
|
|
||||||
|
if acl_content.allow.is_empty() {
|
||||||
return Err!(Request(BadJson(debug_warn!(
|
return Err!(Request(BadJson(debug_warn!(
|
||||||
%room_id,
|
%room_id,
|
||||||
"Sending an ACL event with an empty allow key will permanently \
|
"Sending an ACL event with an empty allow key will permanently \
|
||||||
@@ -248,7 +254,7 @@ async fn allowed_to_send_state_event(
|
|||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if acl_content.deny_contains("*") && acl_content.allow_contains("*") {
|
if allow_has_wildcard && deny_has_wildcard {
|
||||||
return Err!(Request(BadJson(debug_warn!(
|
return Err!(Request(BadJson(debug_warn!(
|
||||||
%room_id,
|
%room_id,
|
||||||
"Sending an ACL event with a deny and allow key value of \"*\" will \
|
"Sending an ACL event with a deny and allow key value of \"*\" will \
|
||||||
@@ -257,9 +263,9 @@ async fn allowed_to_send_state_event(
|
|||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if acl_content.deny_contains("*")
|
if deny_has_wildcard
|
||||||
&& !acl_content.is_allowed(services.globals.server_name())
|
&& !acl_content.is_allowed(services.globals.server_name())
|
||||||
&& !acl_content.allow_contains(services.globals.server_name().as_str())
|
&& !allow_has_server
|
||||||
{
|
{
|
||||||
return Err!(Request(BadJson(debug_warn!(
|
return Err!(Request(BadJson(debug_warn!(
|
||||||
%room_id,
|
%room_id,
|
||||||
@@ -269,9 +275,9 @@ async fn allowed_to_send_state_event(
|
|||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !acl_content.allow_contains("*")
|
if !allow_has_wildcard
|
||||||
&& !acl_content.is_allowed(services.globals.server_name())
|
&& !acl_content.is_allowed(services.globals.server_name())
|
||||||
&& !acl_content.allow_contains(services.globals.server_name().as_str())
|
&& !allow_has_server
|
||||||
{
|
{
|
||||||
return Err!(Request(BadJson(debug_warn!(
|
return Err!(Request(BadJson(debug_warn!(
|
||||||
%room_id,
|
%room_id,
|
||||||
@@ -297,7 +303,7 @@ async fn allowed_to_send_state_event(
|
|||||||
// admin room is a sensitive room, it should not ever be made public
|
// admin room is a sensitive room, it should not ever be made public
|
||||||
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
|
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
|
||||||
if admin_room_id == room_id {
|
if admin_room_id == room_id {
|
||||||
match json.deserialize_as::<RoomJoinRulesEventContent>() {
|
match json.deserialize_as_unchecked::<RoomJoinRulesEventContent>() {
|
||||||
| Ok(join_rule) =>
|
| Ok(join_rule) =>
|
||||||
if join_rule.join_rule == JoinRule::Public {
|
if join_rule.join_rule == JoinRule::Public {
|
||||||
return Err!(Request(Forbidden(
|
return Err!(Request(Forbidden(
|
||||||
@@ -316,7 +322,7 @@ async fn allowed_to_send_state_event(
|
|||||||
| StateEventType::RoomHistoryVisibility => {
|
| StateEventType::RoomHistoryVisibility => {
|
||||||
// admin room is a sensitive room, it should not ever be made world readable
|
// admin room is a sensitive room, it should not ever be made world readable
|
||||||
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
|
if let Ok(admin_room_id) = services.admin.get_admin_room().await {
|
||||||
match json.deserialize_as::<RoomHistoryVisibilityEventContent>() {
|
match json.deserialize_as_unchecked::<RoomHistoryVisibilityEventContent>() {
|
||||||
| Ok(visibility_content) => {
|
| Ok(visibility_content) => {
|
||||||
if admin_room_id == room_id
|
if admin_room_id == room_id
|
||||||
&& visibility_content.history_visibility
|
&& visibility_content.history_visibility
|
||||||
@@ -337,7 +343,7 @@ async fn allowed_to_send_state_event(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
| StateEventType::RoomCanonicalAlias => {
|
| StateEventType::RoomCanonicalAlias => {
|
||||||
match json.deserialize_as::<RoomCanonicalAliasEventContent>() {
|
match json.deserialize_as_unchecked::<RoomCanonicalAliasEventContent>() {
|
||||||
| Ok(canonical_alias_content) => {
|
| Ok(canonical_alias_content) => {
|
||||||
let mut aliases = canonical_alias_content.alt_aliases.clone();
|
let mut aliases = canonical_alias_content.alt_aliases.clone();
|
||||||
|
|
||||||
@@ -369,65 +375,66 @@ async fn allowed_to_send_state_event(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
| StateEventType::RoomMember => match json.deserialize_as::<RoomMemberEventContent>() {
|
| StateEventType::RoomMember =>
|
||||||
| Ok(mut membership_content) => {
|
match json.deserialize_as_unchecked::<RoomMemberEventContent>() {
|
||||||
let Ok(state_key) = UserId::parse(state_key) else {
|
| Ok(mut membership_content) => {
|
||||||
return Err!(Request(BadJson(
|
let Ok(state_key) = UserId::parse(state_key) else {
|
||||||
"Membership event has invalid or non-existent state key"
|
|
||||||
)));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(authorising_user) =
|
|
||||||
membership_content.join_authorized_via_users_server
|
|
||||||
{
|
|
||||||
// join_authorized_via_users_server must be thrown away, if user is already a
|
|
||||||
// member of the room.
|
|
||||||
if services
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.is_joined(state_key, room_id)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
membership_content.join_authorized_via_users_server = None;
|
|
||||||
*json = Raw::<AnyStateEventContent>::from_json_string(
|
|
||||||
serde_json::to_string(&membership_content)?,
|
|
||||||
)?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if membership_content.membership != MembershipState::Join {
|
|
||||||
return Err!(Request(BadJson(
|
return Err!(Request(BadJson(
|
||||||
"join_authorised_via_users_server is only for member joins"
|
"Membership event has invalid or non-existent state key"
|
||||||
)));
|
)));
|
||||||
}
|
};
|
||||||
|
|
||||||
if !services.globals.user_is_local(&authorising_user) {
|
if let Some(authorising_user) =
|
||||||
return Err!(Request(InvalidParam(
|
membership_content.join_authorized_via_users_server
|
||||||
"Authorising user {authorising_user} does not belong to this \
|
|
||||||
homeserver"
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !services
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.is_joined(&authorising_user, room_id)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
return Err!(Request(InvalidParam(
|
// join_authorized_via_users_server must be thrown away, if user is
|
||||||
"Authorising user {authorising_user} is not in the room, they \
|
// already a member of the room.
|
||||||
cannot authorise the join."
|
if services
|
||||||
)));
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.is_joined(&state_key, room_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
membership_content.join_authorized_via_users_server = None;
|
||||||
|
*json = Raw::<AnyStateEventContent>::from_json_string(
|
||||||
|
serde_json::to_string(&membership_content)?,
|
||||||
|
)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if membership_content.membership != MembershipState::Join {
|
||||||
|
return Err!(Request(BadJson(
|
||||||
|
"join_authorised_via_users_server is only for member joins"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !services.globals.user_is_local(&authorising_user) {
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"Authorising user {authorising_user} does not belong to this \
|
||||||
|
homeserver"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !services
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.is_joined(&authorising_user, room_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return Err!(Request(InvalidParam(
|
||||||
|
"Authorising user {authorising_user} is not in the room, they \
|
||||||
|
cannot authorise the join."
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
| Err(e) => {
|
||||||
|
return Err!(Request(BadJson(
|
||||||
|
"Membership content must have a valid JSON body with at least a valid \
|
||||||
|
membership state: {e}"
|
||||||
|
)));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
| Err(e) => {
|
|
||||||
return Err!(Request(BadJson(
|
|
||||||
"Membership content must have a valid JSON body with at least a valid \
|
|
||||||
membership state: {e}"
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
| _ => (),
|
| _ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -118,7 +118,7 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
|||||||
.ruma_route(&client::send_message_event_route)
|
.ruma_route(&client::send_message_event_route)
|
||||||
.ruma_route(&client::send_state_event_for_key_route)
|
.ruma_route(&client::send_state_event_for_key_route)
|
||||||
.ruma_route(&client::get_state_events_route)
|
.ruma_route(&client::get_state_events_route)
|
||||||
.ruma_route(&client::get_state_events_for_key_route)
|
.ruma_route(&client::get_state_event_for_key_route)
|
||||||
// Ruma doesn't have support for multiple paths for a single endpoint yet, and these routes
|
// Ruma doesn't have support for multiple paths for a single endpoint yet, and these routes
|
||||||
// share one Ruma request / response type pair with {get,send}_state_event_for_key_route
|
// share one Ruma request / response type pair with {get,send}_state_event_for_key_route
|
||||||
.route(
|
.route(
|
||||||
|
|||||||
Reference in New Issue
Block a user