|
|
@ -48,7 +48,7 @@ use ruma::{
|
|
|
|
state_res::{self, RoomVersion, StateMap},
|
|
|
|
state_res::{self, RoomVersion, StateMap},
|
|
|
|
to_device::DeviceIdOrAllDevices,
|
|
|
|
to_device::DeviceIdOrAllDevices,
|
|
|
|
uint, EventId, MilliSecondsSinceUnixEpoch, RoomId, RoomVersionId, ServerName,
|
|
|
|
uint, EventId, MilliSecondsSinceUnixEpoch, RoomId, RoomVersionId, ServerName,
|
|
|
|
ServerSigningKeyId, UserId,
|
|
|
|
ServerSigningKeyId,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
use std::{
|
|
|
|
use std::{
|
|
|
|
collections::{btree_map, hash_map, BTreeMap, BTreeSet, HashMap, HashSet},
|
|
|
|
collections::{btree_map, hash_map, BTreeMap, BTreeSet, HashMap, HashSet},
|
|
|
@ -1497,6 +1497,47 @@ async fn upgrade_outlier_to_timeline_pdu(
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.map_err(|_| "Failed to get_auth_events.".to_owned())?;
|
|
|
|
.map_err(|_| "Failed to get_auth_events.".to_owned())?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let state_ids_compressed = state_at_incoming_event
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.map(|(shortstatekey, id)| {
|
|
|
|
|
|
|
|
db.rooms
|
|
|
|
|
|
|
|
.compress_state_event(*shortstatekey, &id, &db.globals)
|
|
|
|
|
|
|
|
.map_err(|_| "Failed to compress_state_event".to_owned())
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.collect::<StdResult<_, String>>()?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 13. Check if the event passes auth based on the "current state" of the room, if not "soft fail" it
|
|
|
|
|
|
|
|
debug!("starting soft fail auth check");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let soft_fail = !state_res::event_auth::auth_check(
|
|
|
|
|
|
|
|
&room_version,
|
|
|
|
|
|
|
|
&incoming_pdu,
|
|
|
|
|
|
|
|
previous_create,
|
|
|
|
|
|
|
|
None,
|
|
|
|
|
|
|
|
|k, s| auth_events.get(&(k.clone(), s.to_owned())).map(Arc::clone),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.map_err(|_e| "Auth check failed.".to_owned())?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if soft_fail {
|
|
|
|
|
|
|
|
append_incoming_pdu(
|
|
|
|
|
|
|
|
&db,
|
|
|
|
|
|
|
|
&incoming_pdu,
|
|
|
|
|
|
|
|
val,
|
|
|
|
|
|
|
|
extremities,
|
|
|
|
|
|
|
|
state_ids_compressed,
|
|
|
|
|
|
|
|
soft_fail,
|
|
|
|
|
|
|
|
&state_lock,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.map_err(|_| "Failed to add pdu to db.".to_owned())?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Soft fail, we keep the event as an outlier but don't add it to the timeline
|
|
|
|
|
|
|
|
warn!("Event was soft failed: {:?}", incoming_pdu);
|
|
|
|
|
|
|
|
db.rooms
|
|
|
|
|
|
|
|
.mark_event_soft_failed(&incoming_pdu.event_id)
|
|
|
|
|
|
|
|
.map_err(|_| "Failed to set soft failed flag".to_owned())?;
|
|
|
|
|
|
|
|
return Err("Event has been soft failed".into());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if incoming_pdu.state_key.is_some() {
|
|
|
|
if incoming_pdu.state_key.is_some() {
|
|
|
|
let mut extremity_sstatehashes = HashMap::new();
|
|
|
|
let mut extremity_sstatehashes = HashMap::new();
|
|
|
|
|
|
|
|
|
|
|
@ -1651,31 +1692,10 @@ async fn upgrade_outlier_to_timeline_pdu(
|
|
|
|
|
|
|
|
|
|
|
|
extremities.insert(incoming_pdu.event_id.clone());
|
|
|
|
extremities.insert(incoming_pdu.event_id.clone());
|
|
|
|
|
|
|
|
|
|
|
|
// 13. Check if the event passes auth based on the "current state" of the room, if not "soft fail" it
|
|
|
|
|
|
|
|
debug!("starting soft fail auth check");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let soft_fail = !state_res::event_auth::auth_check(
|
|
|
|
|
|
|
|
&room_version,
|
|
|
|
|
|
|
|
&incoming_pdu,
|
|
|
|
|
|
|
|
previous_create,
|
|
|
|
|
|
|
|
None,
|
|
|
|
|
|
|
|
|k, s| auth_events.get(&(k.clone(), s.to_owned())).map(Arc::clone),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.map_err(|_e| "Auth check failed.".to_owned())?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Now that the event has passed all auth it is added into the timeline.
|
|
|
|
// Now that the event has passed all auth it is added into the timeline.
|
|
|
|
// We use the `state_at_event` instead of `state_after` so we accurately
|
|
|
|
// We use the `state_at_event` instead of `state_after` so we accurately
|
|
|
|
// represent the state for this event.
|
|
|
|
// represent the state for this event.
|
|
|
|
|
|
|
|
|
|
|
|
let state_ids_compressed = state_at_incoming_event
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.map(|(shortstatekey, id)| {
|
|
|
|
|
|
|
|
db.rooms
|
|
|
|
|
|
|
|
.compress_state_event(*shortstatekey, &id, &db.globals)
|
|
|
|
|
|
|
|
.map_err(|_| "Failed to compress_state_event".to_owned())
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.collect::<StdResult<_, String>>()?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let pdu_id = append_incoming_pdu(
|
|
|
|
let pdu_id = append_incoming_pdu(
|
|
|
|
&db,
|
|
|
|
&db,
|
|
|
|
&incoming_pdu,
|
|
|
|
&incoming_pdu,
|
|
|
@ -1689,15 +1709,6 @@ async fn upgrade_outlier_to_timeline_pdu(
|
|
|
|
|
|
|
|
|
|
|
|
debug!("Appended incoming pdu.");
|
|
|
|
debug!("Appended incoming pdu.");
|
|
|
|
|
|
|
|
|
|
|
|
if soft_fail {
|
|
|
|
|
|
|
|
// Soft fail, we keep the event as an outlier but don't add it to the timeline
|
|
|
|
|
|
|
|
warn!("Event was soft failed: {:?}", incoming_pdu);
|
|
|
|
|
|
|
|
db.rooms
|
|
|
|
|
|
|
|
.mark_event_soft_failed(&incoming_pdu.event_id)
|
|
|
|
|
|
|
|
.map_err(|_| "Failed to set soft failed flag".to_owned())?;
|
|
|
|
|
|
|
|
return Err("Event has been soft failed".into());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Event has passed all auth/stateres checks
|
|
|
|
// Event has passed all auth/stateres checks
|
|
|
|
drop(state_lock);
|
|
|
|
drop(state_lock);
|
|
|
|
Ok(pdu_id)
|
|
|
|
Ok(pdu_id)
|
|
|
@ -2017,6 +2028,11 @@ fn append_incoming_pdu(
|
|
|
|
)?;
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
for appservice in db.appservice.all()? {
|
|
|
|
for appservice in db.appservice.all()? {
|
|
|
|
|
|
|
|
if db.rooms.appservice_in_room(&pdu.room_id, &appservice, db)? {
|
|
|
|
|
|
|
|
db.sending.send_pdu_appservice(&appservice.0, &pdu_id)?;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(namespaces) = appservice.1.get("namespaces") {
|
|
|
|
if let Some(namespaces) = appservice.1.get("namespaces") {
|
|
|
|
let users = namespaces
|
|
|
|
let users = namespaces
|
|
|
|
.get("users")
|
|
|
|
.get("users")
|
|
|
@ -2029,45 +2045,35 @@ fn append_incoming_pdu(
|
|
|
|
});
|
|
|
|
});
|
|
|
|
let aliases = namespaces
|
|
|
|
let aliases = namespaces
|
|
|
|
.get("aliases")
|
|
|
|
.get("aliases")
|
|
|
|
.and_then(|users| users.get("regex"))
|
|
|
|
.and_then(|aliases| aliases.as_sequence())
|
|
|
|
.and_then(|regex| regex.as_str())
|
|
|
|
.map_or_else(Vec::new, |aliases| {
|
|
|
|
.and_then(|regex| Regex::new(regex).ok());
|
|
|
|
aliases
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.filter_map(|aliases| Regex::new(aliases.get("regex")?.as_str()?).ok())
|
|
|
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
|
|
|
});
|
|
|
|
let rooms = namespaces
|
|
|
|
let rooms = namespaces
|
|
|
|
.get("rooms")
|
|
|
|
.get("rooms")
|
|
|
|
.and_then(|rooms| rooms.as_sequence());
|
|
|
|
.and_then(|rooms| rooms.as_sequence());
|
|
|
|
|
|
|
|
|
|
|
|
let room_aliases = db.rooms.room_aliases(&pdu.room_id);
|
|
|
|
let matching_users = |users: &Regex| {
|
|
|
|
|
|
|
|
|
|
|
|
let bridge_user_id = appservice
|
|
|
|
|
|
|
|
.1
|
|
|
|
|
|
|
|
.get("sender_localpart")
|
|
|
|
|
|
|
|
.and_then(|string| string.as_str())
|
|
|
|
|
|
|
|
.and_then(|string| {
|
|
|
|
|
|
|
|
UserId::parse_with_server_name(string, db.globals.server_name()).ok()
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[allow(clippy::blocks_in_if_conditions)]
|
|
|
|
|
|
|
|
if bridge_user_id.map_or(false, |bridge_user_id| {
|
|
|
|
|
|
|
|
db.rooms
|
|
|
|
|
|
|
|
.is_joined(&bridge_user_id, &pdu.room_id)
|
|
|
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
|
|
|
}) || users.iter().any(|users| {
|
|
|
|
|
|
|
|
users.is_match(pdu.sender.as_str())
|
|
|
|
users.is_match(pdu.sender.as_str())
|
|
|
|
|| pdu.kind == EventType::RoomMember
|
|
|
|
|| pdu.kind == EventType::RoomMember
|
|
|
|
&& pdu
|
|
|
|
&& pdu
|
|
|
|
.state_key
|
|
|
|
.state_key
|
|
|
|
.as_ref()
|
|
|
|
.as_ref()
|
|
|
|
.map_or(false, |state_key| users.is_match(&state_key))
|
|
|
|
.map_or(false, |state_key| users.is_match(&state_key))
|
|
|
|
}) || aliases.map_or(false, |aliases| {
|
|
|
|
};
|
|
|
|
room_aliases
|
|
|
|
let matching_aliases = |aliases: &Regex| {
|
|
|
|
|
|
|
|
db.rooms
|
|
|
|
|
|
|
|
.room_aliases(&pdu.room_id)
|
|
|
|
.filter_map(|r| r.ok())
|
|
|
|
.filter_map(|r| r.ok())
|
|
|
|
.any(|room_alias| aliases.is_match(room_alias.as_str()))
|
|
|
|
.any(|room_alias| aliases.is_match(room_alias.as_str()))
|
|
|
|
}) || rooms.map_or(false, |rooms| rooms.contains(&pdu.room_id.as_str().into()))
|
|
|
|
};
|
|
|
|
|| db
|
|
|
|
|
|
|
|
.rooms
|
|
|
|
if aliases.iter().any(matching_aliases)
|
|
|
|
.room_members(&pdu.room_id)
|
|
|
|
|| rooms.map_or(false, |rooms| rooms.contains(&pdu.room_id.as_str().into()))
|
|
|
|
.filter_map(|r| r.ok())
|
|
|
|
|| users.iter().any(matching_users)
|
|
|
|
.any(|member| users.iter().any(|regex| regex.is_match(member.as_str())))
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
db.sending.send_pdu_appservice(&appservice.0, &pdu_id)?;
|
|
|
|
db.sending.send_pdu_appservice(&appservice.0, &pdu_id)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|