general misc bug fixes and slight improvements

Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
strawberry 2024-11-10 21:20:38 -05:00
parent fd2a002480
commit 4f0bdb5194
10 changed files with 127 additions and 100 deletions

View file

@ -1240,8 +1240,8 @@ async fn make_join_request(
) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> { ) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> {
let mut make_join_response_and_server = Err!(BadServerResponse("No server available to assist in joining.")); let mut make_join_response_and_server = Err!(BadServerResponse("No server available to assist in joining."));
let mut make_join_counter: u16 = 0; let mut make_join_counter: usize = 0;
let mut incompatible_room_version_count: u8 = 0; let mut incompatible_room_version_count: usize = 0;
for remote_server in servers { for remote_server in servers {
if services.globals.server_is_ours(remote_server) { if services.globals.server_is_ours(remote_server) {
@ -1264,28 +1264,25 @@ async fn make_join_request(
make_join_counter = make_join_counter.saturating_add(1); make_join_counter = make_join_counter.saturating_add(1);
if let Err(ref e) = make_join_response { if let Err(ref e) = make_join_response {
trace!("make_join ErrorKind string: {:?}", e.kind().to_string()); if matches!(
e.kind(),
// converting to a string is necessary (i think) because ruma is forcing us to ErrorKind::IncompatibleRoomVersion { .. } | ErrorKind::UnsupportedRoomVersion
// fill in the struct for M_INCOMPATIBLE_ROOM_VERSION ) {
if e.kind().to_string().contains("M_INCOMPATIBLE_ROOM_VERSION")
|| e.kind().to_string().contains("M_UNSUPPORTED_ROOM_VERSION")
{
incompatible_room_version_count = incompatible_room_version_count.saturating_add(1); incompatible_room_version_count = incompatible_room_version_count.saturating_add(1);
} }
if incompatible_room_version_count > 15 { if incompatible_room_version_count > 15 {
info!( info!(
"15 servers have responded with M_INCOMPATIBLE_ROOM_VERSION or M_UNSUPPORTED_ROOM_VERSION, \ "15 servers have responded with M_INCOMPATIBLE_ROOM_VERSION or M_UNSUPPORTED_ROOM_VERSION, \
assuming that Conduwuit does not support the room {room_id}: {e}" assuming that conduwuit does not support the room {room_id}: {e}"
); );
make_join_response_and_server = Err!(BadServerResponse("Room version is not supported by Conduwuit")); make_join_response_and_server = Err!(BadServerResponse("Room version is not supported by Conduwuit"));
return make_join_response_and_server; return make_join_response_and_server;
} }
if make_join_counter > 50 { if make_join_counter > 40 {
warn!( warn!(
"50 servers failed to provide valid make_join response, assuming no server can assist in joining." "40 servers failed to provide valid make_join response, assuming no server can assist in joining."
); );
make_join_response_and_server = Err!(BadServerResponse("No server available to assist in joining.")); make_join_response_and_server = Err!(BadServerResponse("No server available to assist in joining."));
return make_join_response_and_server; return make_join_response_and_server;

View file

@ -13,6 +13,7 @@ use ruma::{
profile::{get_avatar_url, get_display_name, get_profile, get_profile_key, get_timezone_key}, profile::{get_avatar_url, get_display_name, get_profile, get_profile_key, get_timezone_key},
voip::get_turn_server_info, voip::get_turn_server_info,
}, },
federation::openid::get_openid_userinfo,
AuthScheme, IncomingRequest, Metadata, AuthScheme, IncomingRequest, Metadata,
}, },
server_util::authorization::XMatrix, server_util::authorization::XMatrix,
@ -102,26 +103,6 @@ pub(super) async fn auth(
} }
match (metadata.authentication, token) { match (metadata.authentication, token) {
(_, Token::Invalid) => {
// OpenID endpoint uses a query param with the same name, drop this once query
// params for user auth are removed from the spec. This is required to make
// integration manager work.
if request.query.access_token.is_some() && request.parts.uri.path().contains("/openid/") {
Ok(Auth {
origin: None,
sender_user: None,
sender_device: None,
appservice_info: None,
})
} else {
Err(Error::BadRequest(
ErrorKind::UnknownToken {
soft_logout: false,
},
"Unknown access token.",
))
}
},
(AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(services, request, info).await?), (AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(services, request, info).await?),
(AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken, Token::Appservice(info)) => { (AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken, Token::Appservice(info)) => {
Ok(Auth { Ok(Auth {
@ -132,7 +113,6 @@ pub(super) async fn auth(
}) })
}, },
(AuthScheme::AccessToken, Token::None) => match metadata { (AuthScheme::AccessToken, Token::None) => match metadata {
// TODO: can we check this better?
&get_turn_server_info::v3::Request::METADATA => { &get_turn_server_info::v3::Request::METADATA => {
if services.globals.config.turn_allow_guests { if services.globals.config.turn_allow_guests {
Ok(Auth { Ok(Auth {
@ -171,6 +151,32 @@ pub(super) async fn auth(
ErrorKind::Unauthorized, ErrorKind::Unauthorized,
"Only appservice access tokens should be used on this endpoint.", "Only appservice access tokens should be used on this endpoint.",
)), )),
(AuthScheme::None, Token::Invalid) => {
// OpenID federation endpoint uses a query param with the same name, drop this
// once query params for user auth are removed from the spec. This is
// required to make integration manager work.
if request.query.access_token.is_some() && metadata == &get_openid_userinfo::v1::Request::METADATA {
Ok(Auth {
origin: None,
sender_user: None,
sender_device: None,
appservice_info: None,
})
} else {
Err(Error::BadRequest(
ErrorKind::UnknownToken {
soft_logout: false,
},
"Unknown access token.",
))
}
},
(_, Token::Invalid) => Err(Error::BadRequest(
ErrorKind::UnknownToken {
soft_logout: false,
},
"Unknown access token.",
)),
} }
} }

View file

@ -1,5 +1,6 @@
use axum::extract::State; use axum::extract::State;
use axum_client_ip::InsecureClientIp; use axum_client_ip::InsecureClientIp;
use base64::{engine::general_purpose, Engine as _};
use conduit::{err, utils, warn, Err, Error, PduEvent, Result}; use conduit::{err, utils, warn, Err, Error, PduEvent, Result};
use ruma::{ use ruma::{
api::{client::error::ErrorKind, federation::membership::create_invite}, api::{client::error::ErrorKind, federation::membership::create_invite},
@ -125,8 +126,10 @@ pub(crate) async fn create_invite_route(
invite_state.push(pdu.to_stripped_state_event()); invite_state.push(pdu.to_stripped_state_event());
// If we are active in the room, the remote server will notify us about the join // If we are active in the room, the remote server will notify us about the
// via /send // join/invite through /send. If we are not in the room, we need to manually
// record the invited state for client /sync through update_membership(), and
// send the invite PDU to the relevant appservices.
if !services if !services
.rooms .rooms
.state_cache .state_cache
@ -148,6 +151,25 @@ pub(crate) async fn create_invite_route(
.await?; .await?;
} }
for appservice in services.appservice.read().await.values() {
if appservice.is_user_match(&invited_user) {
services
.sending
.send_appservice_request(
appservice.registration.clone(),
ruma::api::appservice::event::push_events::v1::Request {
events: vec![pdu.to_room_event()],
txn_id: general_purpose::URL_SAFE_NO_PAD
.encode(utils::calculate_hash(&[pdu.event_id.as_bytes()]))
.into(),
ephemeral: Vec::new(),
to_device: Vec::new(),
},
)
.await?;
}
}
Ok(create_invite::v2::Response { Ok(create_invite::v2::Response {
event: services event: services
.sending .sending

View file

@ -80,6 +80,14 @@ pub(crate) async fn create_join_event_template_route(
} }
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?; let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
if !body.ver.contains(&room_version_id) {
return Err(Error::BadRequest(
ErrorKind::IncompatibleRoomVersion {
room_version: room_version_id,
},
"Room version not supported.",
));
}
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await; let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
@ -118,16 +126,6 @@ pub(crate) async fn create_join_event_template_route(
None None
}; };
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
if !body.ver.contains(&room_version_id) {
return Err(Error::BadRequest(
ErrorKind::IncompatibleRoomVersion {
room_version: room_version_id,
},
"Room version not supported.",
));
}
let (_pdu, mut pdu_json) = services let (_pdu, mut pdu_json) = services
.rooms .rooms
.timeline .timeline

View file

@ -157,7 +157,5 @@ async fn create_leave_event(
.room_servers(room_id) .room_servers(room_id)
.ready_filter(|server| !services.globals.server_is_ours(server)); .ready_filter(|server| !services.globals.server_is_ours(server));
services.sending.send_pdu_servers(servers, &pdu_id).await?; services.sending.send_pdu_servers(servers, &pdu_id).await
Ok(())
} }

View file

@ -8,7 +8,7 @@ use std::{
}; };
use conduit::{ use conduit::{
checked, debug, debug_info, err, checked, debug_info, err,
utils::{math::usize_from_f64, IterStream}, utils::{math::usize_from_f64, IterStream},
Error, Result, Error, Result,
}; };
@ -234,27 +234,25 @@ impl Service {
}); });
} }
Ok( if let Some(children_pdus) = self.get_stripped_space_child_events(current_room).await? {
if let Some(children_pdus) = self.get_stripped_space_child_events(current_room).await? { let summary = self
let summary = self .get_room_summary(current_room, children_pdus, &identifier)
.get_room_summary(current_room, children_pdus, &identifier) .await;
.await; if let Ok(summary) = summary {
if let Ok(summary) = summary { self.roomid_spacehierarchy_cache.lock().await.insert(
self.roomid_spacehierarchy_cache.lock().await.insert( current_room.clone(),
current_room.clone(), Some(CachedSpaceHierarchySummary {
Some(CachedSpaceHierarchySummary { summary: summary.clone(),
summary: summary.clone(), }),
}), );
);
Some(SummaryAccessibility::Accessible(Box::new(summary))) Ok(Some(SummaryAccessibility::Accessible(Box::new(summary))))
} else {
None
}
} else { } else {
None Ok(None)
}, }
) } else {
Ok(None)
}
} }
/// Gets the summary of a space using solely federation /// Gets the summary of a space using solely federation
@ -393,7 +391,7 @@ impl Service {
.is_accessible_child(current_room, &join_rule.clone().into(), identifier, &allowed_room_ids) .is_accessible_child(current_room, &join_rule.clone().into(), identifier, &allowed_room_ids)
.await .await
{ {
debug!("User is not allowed to see room {room_id}"); debug_info!("User is not allowed to see room {room_id}");
// This error will be caught later // This error will be caught later
return Err(Error::BadRequest(ErrorKind::forbidden(), "User is not allowed to see the room")); return Err(Error::BadRequest(ErrorKind::forbidden(), "User is not allowed to see the room"));
} }
@ -615,16 +613,13 @@ impl Service {
&self, current_room: &OwnedRoomId, join_rule: &SpaceRoomJoinRule, identifier: &Identifier<'_>, &self, current_room: &OwnedRoomId, join_rule: &SpaceRoomJoinRule, identifier: &Identifier<'_>,
allowed_room_ids: &Vec<OwnedRoomId>, allowed_room_ids: &Vec<OwnedRoomId>,
) -> bool { ) -> bool {
// Note: unwrap_or_default for bool means false
match identifier { match identifier {
Identifier::ServerName(server_name) => { Identifier::ServerName(server_name) => {
let room_id: &RoomId = current_room;
// Checks if ACLs allow for the server to participate // Checks if ACLs allow for the server to participate
if self if self
.services .services
.event_handler .event_handler
.acl_check(server_name, room_id) .acl_check(server_name, current_room)
.await .await
.is_err() .is_err()
{ {
@ -645,8 +640,9 @@ impl Service {
return true; return true;
} }
}, },
} // Takes care of join rules }
match join_rule { match &join_rule {
SpaceRoomJoinRule::Public | SpaceRoomJoinRule::Knock | SpaceRoomJoinRule::KnockRestricted => true,
SpaceRoomJoinRule::Restricted => { SpaceRoomJoinRule::Restricted => {
for room in allowed_room_ids { for room in allowed_room_ids {
match identifier { match identifier {
@ -664,7 +660,6 @@ impl Service {
} }
false false
}, },
SpaceRoomJoinRule::Public | SpaceRoomJoinRule::Knock | SpaceRoomJoinRule::KnockRestricted => true,
// Invite only, Private, or Custom join rule // Invite only, Private, or Custom join rule
_ => false, _ => false,
} }

View file

@ -295,20 +295,22 @@ impl Service {
} }
#[tracing::instrument(skip_all, level = "debug")] #[tracing::instrument(skip_all, level = "debug")]
pub async fn summary_stripped(&self, invite: &PduEvent) -> Vec<Raw<AnyStrippedStateEvent>> { pub async fn summary_stripped(&self, event: &PduEvent) -> Vec<Raw<AnyStrippedStateEvent>> {
let cells = [ let cells = [
(&StateEventType::RoomCreate, ""), (&StateEventType::RoomCreate, ""),
(&StateEventType::RoomJoinRules, ""), (&StateEventType::RoomJoinRules, ""),
(&StateEventType::RoomCanonicalAlias, ""), (&StateEventType::RoomCanonicalAlias, ""),
(&StateEventType::RoomName, ""), (&StateEventType::RoomName, ""),
(&StateEventType::RoomAvatar, ""), (&StateEventType::RoomAvatar, ""),
(&StateEventType::RoomMember, invite.sender.as_str()), // Add recommended events (&StateEventType::RoomMember, event.sender.as_str()), // Add recommended events
(&StateEventType::RoomEncryption, ""),
(&StateEventType::RoomTopic, ""),
]; ];
let fetches = cells.iter().map(|(event_type, state_key)| { let fetches = cells.iter().map(|(event_type, state_key)| {
self.services self.services
.state_accessor .state_accessor
.room_state_get(&invite.room_id, event_type, state_key) .room_state_get(&event.room_id, event_type, state_key)
}); });
join_all(fetches) join_all(fetches)
@ -316,7 +318,7 @@ impl Service {
.into_iter() .into_iter()
.filter_map(Result::ok) .filter_map(Result::ok)
.map(|e| e.to_stripped_state_event()) .map(|e| e.to_stripped_state_event())
.chain(once(invite.to_stripped_state_event())) .chain(once(event.to_stripped_state_event()))
.collect() .collect()
} }

View file

@ -10,7 +10,7 @@ use conduit::{
err, error, err, error,
pdu::PduBuilder, pdu::PduBuilder,
utils::{math::usize_from_f64, ReadyExt}, utils::{math::usize_from_f64, ReadyExt},
Error, PduEvent, Result, Err, Error, Event, PduEvent, Result,
}; };
use futures::StreamExt; use futures::StreamExt;
use lru_cache::LruCache; use lru_cache::LruCache;
@ -29,7 +29,7 @@ use ruma::{
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
topic::RoomTopicEventContent, topic::RoomTopicEventContent,
}, },
StateEventType, StateEventType, TimelineEventType,
}, },
room::RoomType, room::RoomType,
space::SpaceRoomJoinRule, space::SpaceRoomJoinRule,
@ -408,34 +408,41 @@ impl Service {
pub async fn user_can_redact( pub async fn user_can_redact(
&self, redacts: &EventId, sender: &UserId, room_id: &RoomId, federation: bool, &self, redacts: &EventId, sender: &UserId, room_id: &RoomId, federation: bool,
) -> Result<bool> { ) -> Result<bool> {
if let Ok(event) = self let redacting_event = self.services.timeline.get_pdu(redacts).await;
if redacting_event
.as_ref()
.is_ok_and(|event| event.event_type() == &TimelineEventType::RoomCreate)
{
return Err!(Request(Forbidden("Redacting m.room.create is not safe, forbidding.")));
}
if let Ok(pl_event_content) = self
.room_state_get_content::<RoomPowerLevelsEventContent>(room_id, &StateEventType::RoomPowerLevels, "") .room_state_get_content::<RoomPowerLevelsEventContent>(room_id, &StateEventType::RoomPowerLevels, "")
.await .await
{ {
let event: RoomPowerLevels = event.into(); let pl_event: RoomPowerLevels = pl_event_content.into();
Ok(event.user_can_redact_event_of_other(sender) Ok(pl_event.user_can_redact_event_of_other(sender)
|| event.user_can_redact_own_event(sender) || pl_event.user_can_redact_own_event(sender)
&& if let Ok(pdu) = self.services.timeline.get_pdu(redacts).await { && if let Ok(redacting_event) = redacting_event {
if federation { if federation {
pdu.sender.server_name() == sender.server_name() redacting_event.sender.server_name() == sender.server_name()
} else { } else {
pdu.sender == sender redacting_event.sender == sender
} }
} else { } else {
false false
}) })
} else { } else {
// Falling back on m.room.create to judge power level // Falling back on m.room.create to judge power level
if let Ok(pdu) = self if let Ok(room_create) = self
.room_state_get(room_id, &StateEventType::RoomCreate, "") .room_state_get(room_id, &StateEventType::RoomCreate, "")
.await .await
{ {
Ok(pdu.sender == sender Ok(room_create.sender == sender
|| if let Ok(pdu) = self.services.timeline.get_pdu(redacts).await { || redacting_event
pdu.sender == sender .as_ref()
} else { .is_ok_and(|redacting_event| redacting_event.sender == sender))
false
})
} else { } else {
Err(Error::bad_database( Err(Error::bad_database(
"No m.room.power_levels or m.room.create events in database for room", "No m.room.power_levels or m.room.create events in database for room",
@ -454,7 +461,7 @@ impl Service {
/// Returns an empty vec if not a restricted room /// Returns an empty vec if not a restricted room
pub fn allowed_room_ids(&self, join_rule: JoinRule) -> Vec<OwnedRoomId> { pub fn allowed_room_ids(&self, join_rule: JoinRule) -> Vec<OwnedRoomId> {
let mut room_ids = vec![]; let mut room_ids = Vec::with_capacity(1);
if let JoinRule::Restricted(r) | JoinRule::KnockRestricted(r) = join_rule { if let JoinRule::Restricted(r) | JoinRule::KnockRestricted(r) = join_rule {
for rule in r.allow { for rule in r.allow {
if let AllowRule::RoomMembership(RoomMembership { if let AllowRule::RoomMembership(RoomMembership {

View file

@ -39,7 +39,7 @@ impl super::Service {
.forbidden_remote_server_names .forbidden_remote_server_names
.contains(dest) .contains(dest)
{ {
return Err!(Request(Forbidden(debug_warn!("Federation with this {dest} is not allowed.")))); return Err!(Request(Forbidden(debug_warn!("Federation with {dest} is not allowed."))));
} }
let actual = self.services.resolver.get_actual_dest(dest).await?; let actual = self.services.resolver.get_actual_dest(dest).await?;

View file

@ -235,13 +235,15 @@ impl Service {
fn select_events_current(&self, dest: Destination, statuses: &mut CurTransactionStatus) -> Result<(bool, bool)> { fn select_events_current(&self, dest: Destination, statuses: &mut CurTransactionStatus) -> Result<(bool, bool)> {
let (mut allow, mut retry) = (true, false); let (mut allow, mut retry) = (true, false);
statuses statuses
.entry(dest) .entry(dest.clone()) // TODO: can we avoid cloning?
.and_modify(|e| match e { .and_modify(|e| match e {
TransactionStatus::Failed(tries, time) => { TransactionStatus::Failed(tries, time) => {
// Fail if a request has failed recently (exponential backoff) // Fail if a request has failed recently (exponential backoff)
let min = self.server.config.sender_timeout; let min = self.server.config.sender_timeout;
let max = self.server.config.sender_retry_backoff_limit; let max = self.server.config.sender_retry_backoff_limit;
if continue_exponential_backoff_secs(min, max, time.elapsed(), *tries) { if continue_exponential_backoff_secs(min, max, time.elapsed(), *tries)
&& !matches!(dest, Destination::Appservice(_))
{
allow = false; allow = false;
} else { } else {
retry = true; retry = true;