mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
refactor: Ruma upstreaming, half-baked edition
Co-authored-by: Jade Ellis <jade@ellis.link>
This commit is contained in:
@@ -57,10 +57,6 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
Json(#[from] serde_json::Error),
|
||||
#[error(transparent)]
|
||||
JsParseInt(#[from] ruma::JsParseIntError), // js_int re-export
|
||||
#[error(transparent)]
|
||||
JsTryFromInt(#[from] ruma::JsTryFromIntError), // js_int re-export
|
||||
#[error(transparent)]
|
||||
Path(#[from] axum::extract::rejection::PathRejection),
|
||||
#[error("Mutex poisoned: {0}")]
|
||||
Poison(Cow<'static, str>),
|
||||
@@ -90,7 +86,7 @@ pub enum Error {
|
||||
// ruma/conduwuit
|
||||
#[error("Arithmetic operation failed: {0}")]
|
||||
Arithmetic(Cow<'static, str>),
|
||||
#[error("{0}: {1}")]
|
||||
#[error("{0:?}: {1}")]
|
||||
BadRequest(ruma::api::client::error::ErrorKind, &'static str), //TODO: remove
|
||||
#[error("{0}")]
|
||||
BadServerResponse(Cow<'static, str>),
|
||||
@@ -120,7 +116,7 @@ pub enum Error {
|
||||
Mxid(#[from] ruma::IdParseError),
|
||||
#[error("from {0}: {1}")]
|
||||
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
|
||||
#[error("{0}: {1}")]
|
||||
#[error("{0:?}: {1}")]
|
||||
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>, http::StatusCode),
|
||||
#[error(transparent)]
|
||||
Ruma(#[from] ruma::api::client::error::Error),
|
||||
@@ -167,13 +163,13 @@ impl Error {
|
||||
/// Returns the Matrix error code / error kind
|
||||
#[inline]
|
||||
pub fn kind(&self) -> ruma::api::client::error::ErrorKind {
|
||||
use ruma::api::client::error::ErrorKind::{FeatureDisabled, Unknown};
|
||||
use ruma::api::client::error::ErrorKind::{Unknown, Unrecognized};
|
||||
|
||||
match self {
|
||||
| Self::Federation(_, error) | Self::Ruma(error) =>
|
||||
response::ruma_error_kind(error).clone(),
|
||||
| Self::BadRequest(kind, ..) | Self::Request(kind, ..) => kind.clone(),
|
||||
| Self::FeatureDisabled(..) => FeatureDisabled,
|
||||
| Self::FeatureDisabled(..) => Unrecognized,
|
||||
| _ => Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use http_body_util::Full;
|
||||
use ruma::api::{
|
||||
OutgoingResponse,
|
||||
client::{
|
||||
error::{ErrorBody, ErrorKind},
|
||||
error::{ErrorBody, ErrorKind, StandardErrorBody},
|
||||
uiaa::UiaaResponse,
|
||||
},
|
||||
};
|
||||
@@ -51,15 +51,9 @@ impl From<Error> for UiaaResponse {
|
||||
return Self::AuthResponse(uiaainfo);
|
||||
}
|
||||
|
||||
let body = ErrorBody::Standard {
|
||||
kind: error.kind(),
|
||||
message: error.message(),
|
||||
};
|
||||
let body = ErrorBody::Standard(StandardErrorBody::new(error.kind(), error.message()));
|
||||
|
||||
Self::MatrixError(ruma::api::client::error::Error {
|
||||
status_code: error.status_code(),
|
||||
body,
|
||||
})
|
||||
Self::MatrixError(ruma::api::client::error::Error::new(error.status_code(), body))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +79,7 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
||||
| Unrecognized => StatusCode::METHOD_NOT_ALLOWED,
|
||||
|
||||
// 404
|
||||
| NotFound | NotImplemented | FeatureDisabled | SenderIgnored { .. } =>
|
||||
| NotFound =>
|
||||
StatusCode::NOT_FOUND,
|
||||
|
||||
// 403
|
||||
@@ -93,7 +87,6 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
||||
| ThreepidAuthFailed
|
||||
| UserDeactivated
|
||||
| ThreepidDenied
|
||||
| InviteBlocked
|
||||
| WrongRoomKeysVersion { .. }
|
||||
| UserSuspended
|
||||
| Forbidden { .. } => StatusCode::FORBIDDEN,
|
||||
@@ -108,7 +101,7 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
||||
}
|
||||
|
||||
pub(super) fn ruma_error_message(error: &ruma::api::client::error::Error) -> String {
|
||||
if let ErrorBody::Standard { message, .. } = &error.body {
|
||||
if let ErrorBody::Standard(StandardErrorBody { message, .. }) = &error.body {
|
||||
return message.clone();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::iter::once;
|
||||
|
||||
use ruma::{RoomVersionId, api::client::discovery::get_capabilities::RoomVersionStability};
|
||||
use ruma::{RoomVersionId, api::client::discovery::get_capabilities::v3::RoomVersionStability};
|
||||
|
||||
use crate::{at, is_equal_to};
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ fn matches_sender<E: Event>(event: &E, filter: &RoomEventFilter) -> bool {
|
||||
}
|
||||
|
||||
fn matches_type<E: Event>(event: &E, filter: &RoomEventFilter) -> bool {
|
||||
let kind = event.kind().to_cow_str();
|
||||
let kind = event.kind().to_string();
|
||||
|
||||
if filter.not_types.iter().any(is_equal_to!(&kind)) {
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruma::{CanonicalJsonObject, OwnedEventId, RoomVersionId};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use crate::{Result, err};
|
||||
use crate::{Err, Result, err};
|
||||
|
||||
/// Generates a correct eventId for the incoming pdu.
|
||||
///
|
||||
@@ -24,7 +24,10 @@ pub fn gen_event_id(
|
||||
value: &CanonicalJsonObject,
|
||||
room_version_id: &RoomVersionId,
|
||||
) -> Result<OwnedEventId> {
|
||||
let reference_hash = ruma::signatures::reference_hash(value, room_version_id)?;
|
||||
let Some(rules) = room_version_id.rules() else {
|
||||
return Err!("Cannot generate event ID for unknown room version {room_version_id}")
|
||||
};
|
||||
let reference_hash = ruma::signatures::reference_hash(value, &rules)?;
|
||||
let event_id: OwnedEventId = format!("${reference_hash}").try_into()?;
|
||||
|
||||
Ok(event_id)
|
||||
|
||||
@@ -21,12 +21,12 @@ impl TypeExt for &StateEventType {
|
||||
|
||||
impl TypeExt for TimelineEventType {
|
||||
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
|
||||
(self.into(), state_key.into())
|
||||
(self.to_string().into(), state_key.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExt for &TimelineEventType {
|
||||
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
|
||||
(self.clone().into(), state_key.into())
|
||||
(self.clone().to_string().into(), state_key.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ pub mod event;
|
||||
pub mod pdu;
|
||||
pub mod state_key;
|
||||
pub mod state_res;
|
||||
pub mod versions;
|
||||
|
||||
pub use event::{Event, TypeExt as EventTypeExt};
|
||||
pub use pdu::{Pdu, PduBuilder, PduCount, PduEvent, PduId, RawPduId, ShortId};
|
||||
pub use state_key::StateKey;
|
||||
pub use state_res::{RoomVersion, StateMap, TypeStateKey};
|
||||
pub use state_res::{StateMap, TypeStateKey};
|
||||
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use ruma::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedEventId,
|
||||
events::{EventContent, MessageLikeEventType, StateEventType, TimelineEventType},
|
||||
events::{MessageLikeEventContent, StateEventContent, TimelineEventType},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
|
||||
@@ -33,7 +33,7 @@ type Unsigned = BTreeMap<String, serde_json::Value>;
|
||||
impl Builder {
|
||||
pub fn state<S, T>(state_key: S, content: &T) -> Self
|
||||
where
|
||||
T: EventContent<EventType = StateEventType>,
|
||||
T: StateEventContent,
|
||||
S: Into<StateKey>,
|
||||
{
|
||||
Self {
|
||||
@@ -47,7 +47,7 @@ impl Builder {
|
||||
|
||||
pub fn timeline<T>(content: &T) -> Self
|
||||
where
|
||||
T: EventContent<EventType = MessageLikeEventType>,
|
||||
T: MessageLikeEventContent,
|
||||
{
|
||||
Self {
|
||||
event_type: content.event_type().into(),
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
use ruma::{RoomVersionId, canonical_json::redact_content_in_place};
|
||||
use serde_json::{Value as JsonValue, json, value::to_raw_value};
|
||||
|
||||
use crate::{Error, Result, err, implement};
|
||||
use crate::{Err, Error, Result, err, implement};
|
||||
|
||||
#[implement(super::Pdu)]
|
||||
pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) -> Result {
|
||||
let Some(rules) = room_version_id.rules() else {
|
||||
return Err!("Cannot redact event for unknown room version {room_version_id}")
|
||||
};
|
||||
|
||||
self.unsigned = None;
|
||||
|
||||
let mut content = serde_json::from_str(self.content.get())
|
||||
.map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?;
|
||||
|
||||
redact_content_in_place(&mut content, room_version_id, self.kind.to_string())
|
||||
redact_content_in_place(&mut content, &rules.redaction, self.kind.to_string())
|
||||
.map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?;
|
||||
|
||||
let reason = serde_json::to_value(reason).expect("Failed to preserialize reason");
|
||||
|
||||
@@ -318,7 +318,7 @@ impl TestStore<Pdu> {
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
|
||||
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
@@ -328,7 +328,7 @@ impl TestStore<Pdu> {
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
|
||||
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
@@ -338,7 +338,7 @@ impl TestStore<Pdu> {
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
|
||||
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -5,16 +5,12 @@ use futures::{
|
||||
future::{OptionFuture, join, join3},
|
||||
};
|
||||
use ruma::{
|
||||
Int, OwnedUserId, RoomVersionId, UserId,
|
||||
events::room::{
|
||||
Int, OwnedUserId, RoomVersionId, UserId, events::room::{
|
||||
create::RoomCreateEventContent,
|
||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
member::{MembershipState, ThirdPartyInvite},
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
third_party_invite::RoomThirdPartyInviteEventContent,
|
||||
},
|
||||
int,
|
||||
serde::{Base64, Raw},
|
||||
}, int, room_version_rules::{RoomIdFormatVersion, RoomVersionRules}, serde::Raw,
|
||||
};
|
||||
use serde::{
|
||||
Deserialize,
|
||||
@@ -28,7 +24,6 @@ use super::{
|
||||
deserialize_power_levels, deserialize_power_levels_content_fields,
|
||||
deserialize_power_levels_content_invite, deserialize_power_levels_content_redact,
|
||||
},
|
||||
room_version::RoomVersion,
|
||||
};
|
||||
use crate::{debug, error, trace, warn};
|
||||
|
||||
@@ -65,13 +60,13 @@ pub fn auth_types_for_event(
|
||||
sender: &UserId,
|
||||
state_key: Option<&str>,
|
||||
content: &RawJsonValue,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> serde_json::Result<Vec<(StateEventType, StateKey)>> {
|
||||
if kind == &TimelineEventType::RoomCreate {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let mut auth_types = if room_version.room_ids_as_hashes {
|
||||
let mut auth_types = if room_version.room_id_format == RoomIdFormatVersion::V2 {
|
||||
vec![
|
||||
(StateEventType::RoomPowerLevels, StateKey::new()),
|
||||
(StateEventType::RoomMember, sender.as_str().into()),
|
||||
@@ -120,15 +115,16 @@ pub fn auth_types_for_event(
|
||||
auth_types.push(key);
|
||||
}
|
||||
|
||||
if membership == MembershipState::Invite {
|
||||
if let Some(Ok(t_id)) = content.third_party_invite.map(|t| t.deserialize()) {
|
||||
let key =
|
||||
(StateEventType::RoomThirdPartyInvite, t_id.signed.token.into());
|
||||
if !auth_types.contains(&key) {
|
||||
auth_types.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: restore this once 3pid support isn't broken
|
||||
// if membership == MembershipState::Invite {
|
||||
// if let Some(Ok(t_id)) = content.third_party_invite.map(|t| t.deserialize()) {
|
||||
// let key =
|
||||
// (StateEventType::RoomThirdPartyInvite, t_id.signed.token.into());
|
||||
// if !auth_types.contains(&key) {
|
||||
// auth_types.push(key);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +151,7 @@ pub fn auth_types_for_event(
|
||||
)]
|
||||
#[allow(clippy::suspicious_operation_groupings)]
|
||||
pub async fn auth_check<E, F, Fut>(
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
incoming_event: &E,
|
||||
current_third_party_invite: Option<&E>,
|
||||
fetch_state: F,
|
||||
@@ -218,13 +214,13 @@ where
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if room_version.room_ids_as_hashes && incoming_event.room_id().is_some() {
|
||||
if room_version.room_id_format == RoomIdFormatVersion::V2 && incoming_event.room_id().is_some() {
|
||||
warn!("room create event incorrectly claims to have a room ID when it should not");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !room_version.use_room_create_sender
|
||||
&& !room_version.explicitly_privilege_room_creators
|
||||
if !room_version.authorization.use_room_create_sender
|
||||
&& !room_version.authorization.explicitly_privilege_room_creators
|
||||
{
|
||||
// If content has no creator field, reject
|
||||
if content.creator.is_none() {
|
||||
@@ -305,10 +301,10 @@ where
|
||||
let claims_create_event = incoming_event
|
||||
.auth_events()
|
||||
.any(|id| id == room_create_event.event_id());
|
||||
if room_version.room_ids_as_hashes && claims_create_event {
|
||||
if room_version.room_id_format == RoomIdFormatVersion::V2 && claims_create_event {
|
||||
warn!("event incorrectly references m.room.create event in auth events");
|
||||
return Ok(false);
|
||||
} else if !room_version.room_ids_as_hashes && !claims_create_event {
|
||||
} else if !(room_version.room_id_format == RoomIdFormatVersion::V2) && !claims_create_event {
|
||||
// If the create event is not referenced in the event's auth events, and this is
|
||||
// a v11 room, reject
|
||||
warn!(
|
||||
@@ -332,7 +328,7 @@ where
|
||||
// If the create event content has the field m.federate set to false and the
|
||||
// sender domain of the event does not match the sender domain of the create
|
||||
// event, reject.
|
||||
if !room_version.room_ids_as_hashes
|
||||
if !(room_version.room_id_format == RoomIdFormatVersion::V2)
|
||||
&& !room_create_content.federate
|
||||
&& room_create_event.sender().server_name() != incoming_event.sender().server_name()
|
||||
{
|
||||
@@ -345,7 +341,7 @@ where
|
||||
}
|
||||
|
||||
// Only in some room versions 6 and below
|
||||
if room_version.special_case_aliases_auth {
|
||||
if room_version.authorization.special_case_room_aliases {
|
||||
// 4. If type is m.room.aliases
|
||||
if *incoming_event.event_type() == TimelineEventType::RoomAliases {
|
||||
debug!("starting m.room.aliases check");
|
||||
@@ -486,7 +482,7 @@ where
|
||||
},
|
||||
| _ => {
|
||||
// If no power level event found the creator gets 100 everyone else gets 0
|
||||
let is_creator = if room_version.use_room_create_sender {
|
||||
let is_creator = if room_version.authorization.use_room_create_sender {
|
||||
room_create_event.sender() == sender
|
||||
} else {
|
||||
#[allow(deprecated)]
|
||||
@@ -497,7 +493,7 @@ where
|
||||
if is_creator { int!(100) } else { int!(0) }
|
||||
},
|
||||
};
|
||||
if room_version.explicitly_privilege_room_creators {
|
||||
if room_version.authorization.explicitly_privilege_room_creators {
|
||||
// If the user sent the create event, or is listed in additional_creators, just
|
||||
// give them Int::MAX
|
||||
if sender == room_create_event.sender()
|
||||
@@ -559,7 +555,7 @@ where
|
||||
if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
|
||||
debug!("starting m.room.power_levels check");
|
||||
let mut creators = BTreeSet::new();
|
||||
if room_version.explicitly_privilege_room_creators {
|
||||
if room_version.authorization.explicitly_privilege_room_creators {
|
||||
creators.insert(create_event.sender().to_owned());
|
||||
for creator in room_create_content.additional_creators.iter().flatten() {
|
||||
creators.insert(creator.deserialize()?);
|
||||
@@ -593,7 +589,7 @@ where
|
||||
// the sender of the redaction has the appropriate permissions per the
|
||||
// power levels.
|
||||
|
||||
if room_version.extra_redaction_checks
|
||||
if room_version.authorization.special_case_room_redaction
|
||||
&& *incoming_event.event_type() == TimelineEventType::RoomRedaction
|
||||
{
|
||||
let redact_level = match power_levels_event {
|
||||
@@ -618,7 +614,7 @@ where
|
||||
}
|
||||
|
||||
fn is_creator<EV>(
|
||||
v: &RoomVersion,
|
||||
v: &RoomVersionRules,
|
||||
c: &BTreeSet<OwnedUserId>,
|
||||
ce: &EV,
|
||||
user_id: &UserId,
|
||||
@@ -627,9 +623,9 @@ fn is_creator<EV>(
|
||||
where
|
||||
EV: Event + Send + Sync,
|
||||
{
|
||||
if v.explicitly_privilege_room_creators {
|
||||
if v.authorization.explicitly_privilege_room_creators {
|
||||
c.contains(user_id)
|
||||
} else if v.use_room_create_sender && !have_pls {
|
||||
} else if v.authorization.use_room_create_sender && !have_pls {
|
||||
ce.sender() == user_id
|
||||
} else if !have_pls {
|
||||
#[allow(deprecated)]
|
||||
@@ -659,7 +655,7 @@ where
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn valid_membership_change<E>(
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
target_user: &UserId,
|
||||
target_user_membership_event: Option<&E>,
|
||||
sender: &UserId,
|
||||
@@ -700,7 +696,7 @@ where
|
||||
|
||||
let power_levels: RoomPowerLevelsEventContent = match &power_levels_event {
|
||||
| Some(ev) => from_json_str(ev.content().get())?,
|
||||
| None => RoomPowerLevelsEventContent::default(),
|
||||
| None => RoomPowerLevelsEventContent::new(&room_version.authorization),
|
||||
};
|
||||
|
||||
let mut sender_power = power_levels
|
||||
@@ -714,7 +710,7 @@ where
|
||||
|
||||
let mut creators = BTreeSet::new();
|
||||
creators.insert(create_room.sender().to_owned());
|
||||
if room_version.explicitly_privilege_room_creators {
|
||||
if room_version.authorization.explicitly_privilege_room_creators {
|
||||
// Explicitly privilege room creators
|
||||
// If the sender sent the create event, or in additional_creators, give them
|
||||
// Int::MAX. Same case for target.
|
||||
@@ -865,7 +861,7 @@ where
|
||||
trace!(sender=%sender, "sender is invited to room, allowing join");
|
||||
true
|
||||
},
|
||||
| JoinRule::Knock if !room_version.allow_knocking => {
|
||||
| JoinRule::Knock if !room_version.authorization.knocking => {
|
||||
warn!("Join rule is knock but room version does not allow knocking");
|
||||
false
|
||||
},
|
||||
@@ -882,7 +878,7 @@ where
|
||||
trace!(sender=%sender, "sender is invited or already joined to room, allowing join");
|
||||
true
|
||||
},
|
||||
| JoinRule::KnockRestricted(_) if !room_version.knock_restricted_join_rule =>
|
||||
| JoinRule::KnockRestricted(_) if !room_version.authorization.knock_restricted_join_rule =>
|
||||
{
|
||||
warn!(
|
||||
"Join rule is knock_restricted but room version does not support it"
|
||||
@@ -1127,7 +1123,7 @@ where
|
||||
}
|
||||
allow
|
||||
},
|
||||
| MembershipState::Knock if room_version.allow_knocking => {
|
||||
| MembershipState::Knock if room_version.authorization.knocking => {
|
||||
// 1. If the `join_rule` is anything other than `knock` or `knock_restricted`,
|
||||
// reject.
|
||||
if !matches!(join_rules, JoinRule::KnockRestricted(_) | JoinRule::Knock) {
|
||||
@@ -1136,7 +1132,7 @@ where
|
||||
);
|
||||
false
|
||||
} else if matches!(join_rules, JoinRule::KnockRestricted(_))
|
||||
&& !room_version.knock_restricted_join_rule
|
||||
&& !room_version.authorization.knock_restricted_join_rule
|
||||
{
|
||||
// 2. If the `join_rule` is `knock_restricted`, but the room does not support
|
||||
// `knock_restricted`, reject.
|
||||
@@ -1226,7 +1222,7 @@ fn can_send_event(event: &impl Event, ple: Option<&impl Event>, user_level: Int)
|
||||
/// Confirm that the event sender has the required power levels.
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn check_power_levels(
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
power_event: &impl Event,
|
||||
previous_power_event: Option<&impl Event>,
|
||||
user_level: Int,
|
||||
@@ -1382,7 +1378,7 @@ fn check_power_levels(
|
||||
}
|
||||
|
||||
// Notifications, currently there is only @room
|
||||
if room_version.limit_notifications_power_levels {
|
||||
if room_version.authorization.limit_notifications_power_levels {
|
||||
let old_level = old_state.notifications.room;
|
||||
let new_level = new_state.notifications.room;
|
||||
if old_level != new_level {
|
||||
@@ -1451,7 +1447,7 @@ fn get_deserialize_levels(
|
||||
/// Does the event redacting come from a user with enough power to redact the
|
||||
/// given event.
|
||||
fn check_redaction(
|
||||
_room_version: &RoomVersion,
|
||||
_room_version: &RoomVersionRules,
|
||||
redaction_event: &impl Event,
|
||||
user_level: Int,
|
||||
redact_level: Int,
|
||||
@@ -1501,79 +1497,32 @@ fn get_send_level(
|
||||
}
|
||||
|
||||
fn verify_third_party_invite(
|
||||
target_user: Option<&UserId>,
|
||||
sender: &UserId,
|
||||
tp_id: &ThirdPartyInvite,
|
||||
current_third_party_invite: Option<&impl Event>,
|
||||
_target_user: Option<&UserId>,
|
||||
_sender: &UserId,
|
||||
_tp_id: &ThirdPartyInvite,
|
||||
_current_third_party_invite: Option<&impl Event>,
|
||||
) -> bool {
|
||||
// 1. Check for user being banned happens before this is called
|
||||
// checking for mxid and token keys is done by ruma when deserializing
|
||||
|
||||
// The state key must match the invitee
|
||||
if target_user != Some(&tp_id.signed.mxid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is no m.room.third_party_invite event in the current room state with
|
||||
// state_key matching token, reject
|
||||
#[allow(clippy::manual_let_else)]
|
||||
let current_tpid = match current_third_party_invite {
|
||||
| Some(id) => id,
|
||||
| None => return false,
|
||||
};
|
||||
|
||||
if current_tpid.state_key() != Some(&tp_id.signed.token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if sender != current_tpid.sender() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If any signature in signed matches any public key in the
|
||||
// m.room.third_party_invite event, allow
|
||||
#[allow(clippy::manual_let_else)]
|
||||
let tpid_ev =
|
||||
match from_json_str::<RoomThirdPartyInviteEventContent>(current_tpid.content().get()) {
|
||||
| Ok(ev) => ev,
|
||||
| Err(_) => return false,
|
||||
};
|
||||
|
||||
#[allow(clippy::manual_let_else)]
|
||||
let decoded_invite_token = match Base64::parse(&tp_id.signed.token) {
|
||||
| Ok(tok) => tok,
|
||||
// FIXME: Log a warning?
|
||||
| Err(_) => return false,
|
||||
};
|
||||
|
||||
// A list of public keys in the public_keys field
|
||||
for key in tpid_ev.public_keys.unwrap_or_default() {
|
||||
if key.public_key == decoded_invite_token {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// A single public key in the public_key field
|
||||
tpid_ev.public_key == decoded_invite_token
|
||||
// TODO: implement proper verification here
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ruma::events::{
|
||||
use ruma::{events::{
|
||||
StateEventType, TimelineEventType,
|
||||
room::{
|
||||
join_rules::{
|
||||
AllowRule, JoinRule, Restricted, RoomJoinRulesEventContent, RoomMembership,
|
||||
AllowRule, JoinRule, Restricted, RoomJoinRulesEventContent,
|
||||
},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
};
|
||||
}, room::RoomMembership, room_version_rules::RoomVersionRules};
|
||||
use serde_json::value::to_raw_value as to_raw_json_value;
|
||||
|
||||
use crate::{
|
||||
matrix::{Event, EventTypeExt, Pdu as PduEvent},
|
||||
state_res::{
|
||||
RoomVersion, StateMap,
|
||||
StateMap,
|
||||
event_auth::valid_membership_change,
|
||||
test_utils::{
|
||||
INITIAL_EVENTS, INITIAL_EVENTS_CREATE_ROOM, alice, charlie, ella, event_id,
|
||||
@@ -1610,7 +1559,7 @@ mod tests {
|
||||
|
||||
assert!(
|
||||
valid_membership_change(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1655,7 +1604,7 @@ mod tests {
|
||||
|
||||
assert!(
|
||||
!valid_membership_change(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1700,7 +1649,7 @@ mod tests {
|
||||
|
||||
assert!(
|
||||
valid_membership_change(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1745,7 +1694,7 @@ mod tests {
|
||||
|
||||
assert!(
|
||||
!valid_membership_change(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1807,7 +1756,7 @@ mod tests {
|
||||
|
||||
assert!(
|
||||
valid_membership_change(
|
||||
&RoomVersion::V9,
|
||||
&RoomVersionRules::V9,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1825,7 +1774,7 @@ mod tests {
|
||||
|
||||
assert!(
|
||||
!valid_membership_change(
|
||||
&RoomVersion::V9,
|
||||
&RoomVersionRules::V9,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
@@ -1879,7 +1828,7 @@ mod tests {
|
||||
|
||||
assert!(
|
||||
valid_membership_change(
|
||||
&RoomVersion::V7,
|
||||
&RoomVersionRules::V7,
|
||||
target_user,
|
||||
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
|
||||
sender,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
pub(crate) mod error;
|
||||
pub mod event_auth;
|
||||
mod power_levels;
|
||||
mod room_version;
|
||||
mod serde_backports;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
@@ -20,25 +20,21 @@ use std::{
|
||||
|
||||
use futures::{Future, FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt, future};
|
||||
use ruma::{
|
||||
EventId, Int, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId,
|
||||
EventId, Int, MilliSecondsSinceUnixEpoch, OwnedEventId,
|
||||
events::{
|
||||
StateEventType, TimelineEventType,
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
int,
|
||||
int, room_version_rules::{RoomIdFormatVersion, RoomVersionRules, StateResolutionVersion},
|
||||
};
|
||||
use serde_json::from_str as from_json_str;
|
||||
|
||||
pub(crate) use self::error::Error;
|
||||
use self::power_levels::PowerLevelsContentFields;
|
||||
pub use self::{
|
||||
event_auth::{auth_check, auth_types_for_event},
|
||||
room_version::RoomVersion,
|
||||
};
|
||||
pub use self::event_auth::{auth_check, auth_types_for_event};
|
||||
use crate::{
|
||||
debug, debug_error, err,
|
||||
matrix::{Event, StateKey},
|
||||
state_res::room_version::StateResolutionVersion,
|
||||
trace,
|
||||
utils::stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt, WidebandExt},
|
||||
warn,
|
||||
@@ -77,7 +73,7 @@ type Result<T, E = Error> = crate::Result<T, E>;
|
||||
//#[tracing::instrument(level event_fetch))]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub async fn resolve<'a, Pdu, Sets, SetIter, Hasher, Fetch, FetchFut, Exists, ExistsFut>(
|
||||
room_version: &RoomVersionId,
|
||||
room_version: &RoomVersionRules,
|
||||
state_sets: Sets,
|
||||
auth_chain_sets: &'a [HashSet<OwnedEventId, Hasher>],
|
||||
event_fetch: &Fetch,
|
||||
@@ -94,11 +90,7 @@ where
|
||||
Pdu: Event + Clone + Send + Sync,
|
||||
for<'b> &'b Pdu: Event + Send,
|
||||
{
|
||||
use RoomVersionId::*;
|
||||
let stateres_version = match room_version {
|
||||
| V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 | V11 => StateResolutionVersion::V2,
|
||||
| _ => StateResolutionVersion::V2_1,
|
||||
};
|
||||
let stateres_version = room_version.state_res;
|
||||
debug!(version = ?stateres_version, "State resolution starting");
|
||||
|
||||
// Split non-conflicting and conflicting state
|
||||
@@ -115,7 +107,7 @@ where
|
||||
debug!(count = conflicting.len(), "conflicting events");
|
||||
trace!(map = ?conflicting, "conflicting events");
|
||||
let (conflicted_state_subgraph, initial_state) =
|
||||
if stateres_version == StateResolutionVersion::V2_1 {
|
||||
if let StateResolutionVersion::V2(v2_rules) = stateres_version && v2_rules.consider_conflicted_state_subgraph {
|
||||
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
|
||||
.await
|
||||
.ok_or_else(|| {
|
||||
@@ -166,7 +158,6 @@ where
|
||||
debug!(count = sorted_control_levels.len(), "power events");
|
||||
trace!(list = ?sorted_control_levels, "sorted power events");
|
||||
|
||||
let room_version = RoomVersion::new(room_version)?;
|
||||
// Sequentially auth check each control event.
|
||||
let resolved_control = iterative_auth_check(
|
||||
&room_version,
|
||||
@@ -596,7 +587,7 @@ where
|
||||
/// `event_auth::auth_check` function.
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
async fn iterative_auth_check<'a, E, F, Fut, S>(
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
events_to_check: S,
|
||||
unconflicted_state: StateMap<OwnedEventId>,
|
||||
fetch_event: &F,
|
||||
@@ -666,7 +657,7 @@ where
|
||||
trace!(list = ?auth_types, event_id = event.event_id().as_str(), "auth types for event");
|
||||
|
||||
let mut auth_state = StateMap::new();
|
||||
if room_version.room_ids_as_hashes {
|
||||
if room_version.room_id_format == RoomIdFormatVersion::V2 {
|
||||
trace!("room version uses hashed IDs, manually fetching create event");
|
||||
let create_event_id_raw = event.room_id_or_hash().as_str().replace('!', "$");
|
||||
let create_event_id = EventId::parse(&create_event_id_raw).map_err(|e| {
|
||||
@@ -963,7 +954,7 @@ impl EventTypeExt for StateEventType {
|
||||
|
||||
impl EventTypeExt for TimelineEventType {
|
||||
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
|
||||
(self.into(), state_key.into())
|
||||
(self.to_string().into(), state_key.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,18 +974,15 @@ mod tests {
|
||||
use maplit::{hashmap, hashset};
|
||||
use rand::seq::SliceRandom;
|
||||
use ruma::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId,
|
||||
events::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId, events::{
|
||||
StateEventType, TimelineEventType,
|
||||
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
},
|
||||
int, uint,
|
||||
}, int, room_version_rules::RoomVersionRules, uint
|
||||
};
|
||||
use serde_json::{json, value::to_raw_value as to_raw_json_value};
|
||||
|
||||
use super::{
|
||||
StateMap, is_power_event,
|
||||
room_version::RoomVersion,
|
||||
test_utils::{
|
||||
INITIAL_EVENTS, TestStore, alice, bob, charlie, do_check, ella, event_id,
|
||||
member_content_ban, member_content_join, room_id, to_init_pdu_event, to_pdu_event,
|
||||
@@ -1004,7 +992,6 @@ mod tests {
|
||||
use crate::{
|
||||
debug,
|
||||
matrix::{Event, EventTypeExt, Pdu as PduEvent},
|
||||
state_res::room_version::StateResolutionVersion,
|
||||
utils::stream::IterStream,
|
||||
};
|
||||
|
||||
@@ -1036,7 +1023,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let resolved_power = super::iterative_auth_check(
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
sorted_power_events.iter().map(AsRef::as_ref).stream(),
|
||||
HashMap::new(), // unconflicted events
|
||||
&fetcher,
|
||||
@@ -1437,7 +1424,7 @@ mod tests {
|
||||
.collect();
|
||||
|
||||
let resolved =
|
||||
match super::resolve(&RoomVersionId::V2, &state_sets, &auth_chain, &fetcher, &exists)
|
||||
match super::resolve(&RoomVersionRules::V2, &state_sets, &auth_chain, &fetcher, &exists)
|
||||
.await
|
||||
{
|
||||
| Ok(state) => state,
|
||||
@@ -1550,7 +1537,7 @@ mod tests {
|
||||
let fetcher = |id: OwnedEventId| ready(ev_map.get(&id).cloned());
|
||||
let exists = |id: OwnedEventId| ready(ev_map.get(&id).is_some());
|
||||
let resolved =
|
||||
match super::resolve(&RoomVersionId::V6, &state_sets, &auth_chain, &fetcher, &exists)
|
||||
match super::resolve(&RoomVersionRules::V6, &state_sets, &auth_chain, &fetcher, &exists)
|
||||
.await
|
||||
{
|
||||
| Ok(state) => state,
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use ruma::{
|
||||
Int, OwnedUserId, UserId,
|
||||
events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent},
|
||||
power_levels::{NotificationPowerLevels, default_power_level},
|
||||
serde::{
|
||||
deserialize_v1_powerlevel, vec_deserialize_int_powerlevel_values,
|
||||
vec_deserialize_v1_powerlevel_values,
|
||||
},
|
||||
Int, OwnedUserId, UserId, events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent}, power_levels::{NotificationPowerLevels, default_power_level}, room_version_rules::{AuthorizationRules, RoomVersionRules}, serde::deserialize_v1_powerlevel
|
||||
};
|
||||
use super::serde_backports::*;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{Error, from_str as from_json_str};
|
||||
|
||||
use super::{Result, RoomVersion};
|
||||
use super::Result;
|
||||
use crate::error;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -48,8 +43,8 @@ struct IntRoomPowerLevelsEventContent {
|
||||
notifications: IntNotificationPowerLevels,
|
||||
}
|
||||
|
||||
impl From<IntRoomPowerLevelsEventContent> for RoomPowerLevelsEventContent {
|
||||
fn from(int_pl: IntRoomPowerLevelsEventContent) -> Self {
|
||||
impl IntRoomPowerLevelsEventContent {
|
||||
fn to_room_power_levels_content(self, auth_rules: &AuthorizationRules) -> RoomPowerLevelsEventContent {
|
||||
let IntRoomPowerLevelsEventContent {
|
||||
ban,
|
||||
events,
|
||||
@@ -61,9 +56,9 @@ impl From<IntRoomPowerLevelsEventContent> for RoomPowerLevelsEventContent {
|
||||
users,
|
||||
users_default,
|
||||
notifications,
|
||||
} = int_pl;
|
||||
} = self;
|
||||
|
||||
let mut pl = Self::new();
|
||||
let mut pl = RoomPowerLevelsEventContent::new(auth_rules);
|
||||
pl.ban = ban;
|
||||
pl.events = events;
|
||||
pl.events_default = events_default;
|
||||
@@ -101,18 +96,18 @@ impl From<IntNotificationPowerLevels> for NotificationPowerLevels {
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_power_levels(
|
||||
content: &str,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> Option<RoomPowerLevelsEventContent> {
|
||||
if room_version.integer_power_levels {
|
||||
deserialize_integer_power_levels(content)
|
||||
if room_version.authorization.integer_power_levels {
|
||||
deserialize_integer_power_levels(content, &room_version.authorization)
|
||||
} else {
|
||||
deserialize_legacy_power_levels(content)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_integer_power_levels(content: &str) -> Option<RoomPowerLevelsEventContent> {
|
||||
fn deserialize_integer_power_levels(content: &str, auth_rules: &AuthorizationRules) -> Option<RoomPowerLevelsEventContent> {
|
||||
match from_json_str::<IntRoomPowerLevelsEventContent>(content) {
|
||||
| Ok(content) => Some(content.into()),
|
||||
| Ok(content) => Some(content.to_room_power_levels_content(auth_rules)),
|
||||
| Err(_) => {
|
||||
error!("m.room.power_levels event is not valid with integer values");
|
||||
None
|
||||
@@ -174,9 +169,9 @@ impl From<IntPowerLevelsContentFields> for PowerLevelsContentFields {
|
||||
#[inline]
|
||||
pub(crate) fn deserialize_power_levels_content_fields(
|
||||
content: &str,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> Result<PowerLevelsContentFields, Error> {
|
||||
if room_version.integer_power_levels {
|
||||
if room_version.authorization.integer_power_levels {
|
||||
deserialize_integer_power_levels_content_fields(content)
|
||||
} else {
|
||||
deserialize_legacy_power_levels_content_fields(content)
|
||||
@@ -216,9 +211,9 @@ impl From<IntPowerLevelsContentInvite> for PowerLevelsContentInvite {
|
||||
|
||||
pub(crate) fn deserialize_power_levels_content_invite(
|
||||
content: &str,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> Result<PowerLevelsContentInvite, Error> {
|
||||
if room_version.integer_power_levels {
|
||||
if room_version.authorization.integer_power_levels {
|
||||
from_json_str::<IntPowerLevelsContentInvite>(content).map(Into::into)
|
||||
} else {
|
||||
from_json_str(content)
|
||||
@@ -246,9 +241,9 @@ impl From<IntPowerLevelsContentRedact> for PowerLevelsContentRedact {
|
||||
|
||||
pub(crate) fn deserialize_power_levels_content_redact(
|
||||
content: &str,
|
||||
room_version: &RoomVersion,
|
||||
room_version: &RoomVersionRules,
|
||||
) -> Result<PowerLevelsContentRedact, Error> {
|
||||
if room_version.integer_power_levels {
|
||||
if room_version.authorization.integer_power_levels {
|
||||
from_json_str::<IntPowerLevelsContentRedact>(content).map(Into::into)
|
||||
} else {
|
||||
from_json_str(content)
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
use ruma::RoomVersionId;
|
||||
|
||||
use super::{Error, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::exhaustive_enums)]
|
||||
pub enum RoomDisposition {
|
||||
/// A room version that has a stable specification.
|
||||
Stable,
|
||||
/// A room version that is not yet fully specified.
|
||||
Unstable,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub enum EventFormatVersion {
|
||||
/// $id:server event id format
|
||||
V1,
|
||||
/// MSC1659-style $hash event id format: introduced for room v3
|
||||
V2,
|
||||
/// MSC1884-style $hash format: introduced for room v4
|
||||
V3,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub enum StateResolutionVersion {
|
||||
/// State resolution for rooms at version 1.
|
||||
V1,
|
||||
/// State resolution for room at version 2 or later.
|
||||
V2,
|
||||
/// State resolution for room at version 12 or later.
|
||||
V2_1,
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct RoomVersion {
|
||||
/// The stability of this room.
|
||||
pub disposition: RoomDisposition,
|
||||
/// The format of the EventId.
|
||||
pub event_format: EventFormatVersion,
|
||||
/// Which state resolution algorithm is used.
|
||||
pub state_res: StateResolutionVersion,
|
||||
// FIXME: not sure what this one means?
|
||||
pub enforce_key_validity: bool,
|
||||
|
||||
/// `m.room.aliases` had special auth rules and redaction rules
|
||||
/// before room version 6.
|
||||
///
|
||||
/// before MSC2261/MSC2432,
|
||||
pub special_case_aliases_auth: bool,
|
||||
/// Strictly enforce canonical json, do not allow:
|
||||
/// * Integers outside the range of [-2 ^ 53 + 1, 2 ^ 53 - 1]
|
||||
/// * Floats
|
||||
/// * NaN, Infinity, -Infinity
|
||||
pub strict_canonicaljson: bool,
|
||||
/// Verify notifications key while checking m.room.power_levels.
|
||||
///
|
||||
/// bool: MSC2209: Check 'notifications'
|
||||
pub limit_notifications_power_levels: bool,
|
||||
/// Extra rules when verifying redaction events.
|
||||
pub extra_redaction_checks: bool,
|
||||
/// Allow knocking in event authentication.
|
||||
///
|
||||
/// See [room v7 specification](https://spec.matrix.org/latest/rooms/v7/)
|
||||
pub allow_knocking: bool,
|
||||
/// Adds support for the restricted join rule.
|
||||
///
|
||||
/// See: [MSC3289](https://github.com/matrix-org/matrix-spec-proposals/pull/3289)
|
||||
pub restricted_join_rules: bool,
|
||||
/// Adds support for the knock_restricted join rule.
|
||||
///
|
||||
/// See: [MSC3787](https://github.com/matrix-org/matrix-spec-proposals/pull/3787)
|
||||
pub knock_restricted_join_rule: bool,
|
||||
/// Enforces integer power levels.
|
||||
///
|
||||
/// See: [MSC3667](https://github.com/matrix-org/matrix-spec-proposals/pull/3667)
|
||||
pub integer_power_levels: bool,
|
||||
/// Determine the room creator using the `m.room.create` event's `sender`,
|
||||
/// instead of the event content's `creator` field.
|
||||
///
|
||||
/// See: [MSC2175](https://github.com/matrix-org/matrix-spec-proposals/pull/2175)
|
||||
pub use_room_create_sender: bool,
|
||||
/// Whether the room creators are considered superusers.
|
||||
/// A superuser will always have infinite power levels in the room.
|
||||
///
|
||||
/// See: [MSC4289](https://github.com/matrix-org/matrix-spec-proposals/pull/4289)
|
||||
pub explicitly_privilege_room_creators: bool,
|
||||
/// Whether the room's m.room.create event ID is itself the room ID.
|
||||
///
|
||||
/// See: [MSC4291](https://github.com/matrix-org/matrix-spec-proposals/pull/4291)
|
||||
pub room_ids_as_hashes: bool,
|
||||
}
|
||||
|
||||
impl RoomVersion {
|
||||
pub const V1: Self = Self {
|
||||
disposition: RoomDisposition::Stable,
|
||||
event_format: EventFormatVersion::V1,
|
||||
state_res: StateResolutionVersion::V1,
|
||||
enforce_key_validity: false,
|
||||
special_case_aliases_auth: true,
|
||||
strict_canonicaljson: false,
|
||||
limit_notifications_power_levels: false,
|
||||
extra_redaction_checks: true,
|
||||
allow_knocking: false,
|
||||
restricted_join_rules: false,
|
||||
knock_restricted_join_rule: false,
|
||||
integer_power_levels: false,
|
||||
use_room_create_sender: false,
|
||||
explicitly_privilege_room_creators: false,
|
||||
room_ids_as_hashes: false,
|
||||
};
|
||||
pub const V10: Self = Self {
|
||||
knock_restricted_join_rule: true,
|
||||
integer_power_levels: true,
|
||||
..Self::V9
|
||||
};
|
||||
pub const V11: Self = Self {
|
||||
use_room_create_sender: true,
|
||||
..Self::V10
|
||||
};
|
||||
pub const V12: Self = Self {
|
||||
explicitly_privilege_room_creators: true,
|
||||
room_ids_as_hashes: true,
|
||||
..Self::V11
|
||||
};
|
||||
pub const V2: Self = Self {
|
||||
state_res: StateResolutionVersion::V2,
|
||||
..Self::V1
|
||||
};
|
||||
pub const V3: Self = Self {
|
||||
event_format: EventFormatVersion::V2,
|
||||
extra_redaction_checks: false,
|
||||
..Self::V2
|
||||
};
|
||||
pub const V4: Self = Self {
|
||||
event_format: EventFormatVersion::V3,
|
||||
..Self::V3
|
||||
};
|
||||
pub const V5: Self = Self { enforce_key_validity: true, ..Self::V4 };
|
||||
pub const V6: Self = Self {
|
||||
special_case_aliases_auth: false,
|
||||
strict_canonicaljson: true,
|
||||
limit_notifications_power_levels: true,
|
||||
..Self::V5
|
||||
};
|
||||
pub const V7: Self = Self { allow_knocking: true, ..Self::V6 };
|
||||
pub const V8: Self = Self { restricted_join_rules: true, ..Self::V7 };
|
||||
pub const V9: Self = Self::V8;
|
||||
|
||||
pub fn new(version: &RoomVersionId) -> Result<Self> {
|
||||
Ok(match version {
|
||||
| RoomVersionId::V1 => Self::V1,
|
||||
| RoomVersionId::V2 => Self::V2,
|
||||
| RoomVersionId::V3 => Self::V3,
|
||||
| RoomVersionId::V4 => Self::V4,
|
||||
| RoomVersionId::V5 => Self::V5,
|
||||
| RoomVersionId::V6 => Self::V6,
|
||||
| RoomVersionId::V7 => Self::V7,
|
||||
| RoomVersionId::V8 => Self::V8,
|
||||
| RoomVersionId::V9 => Self::V9,
|
||||
| RoomVersionId::V10 => Self::V10,
|
||||
| RoomVersionId::V11 => Self::V11,
|
||||
| RoomVersionId::V12 => Self::V12,
|
||||
| ver => return Err(Error::Unsupported(format!("found version `{ver}`"))),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
//! These functions are copied from an old version of Ruma. power_levels.rs uses them to lazily deserialize power level events.
|
||||
//! Upstream Ruma uses a much more elegant approach in its state resolution code, which we may want
|
||||
//! to look into at some point.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Deserializer, de::{MapAccess, Visitor}};
|
||||
use ruma::{Int, serde::deserialize_v1_powerlevel};
|
||||
|
||||
/// Take a Map with values of either an integer number or a string and deserialize
|
||||
/// those to integer numbers in a Vec of sorted pairs.
|
||||
///
|
||||
/// To be used like this:
|
||||
/// `#[serde(deserialize_with = "vec_deserialize_v1_powerlevel_values")]`
|
||||
pub fn vec_deserialize_v1_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: Deserialize<'de> + Ord,
|
||||
{
|
||||
#[repr(transparent)]
|
||||
struct IntWrap(Int);
|
||||
|
||||
impl<'de> Deserialize<'de> for IntWrap {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_v1_powerlevel(deserializer).map(IntWrap)
|
||||
}
|
||||
}
|
||||
|
||||
struct IntMapVisitor<T> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> IntMapVisitor<T> {
|
||||
fn new() -> Self {
|
||||
Self { _phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
|
||||
where
|
||||
T: Deserialize<'de> + Ord,
|
||||
{
|
||||
type Value = Vec<(T, Int)>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a map with integers or strings as values")
|
||||
}
|
||||
|
||||
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
||||
let mut res = Vec::new();
|
||||
if let Some(hint) = map.size_hint() {
|
||||
res.reserve(hint);
|
||||
}
|
||||
|
||||
while let Some((k, IntWrap(v))) = map.next_entry()? {
|
||||
res.push((k, v));
|
||||
}
|
||||
|
||||
res.sort_unstable();
|
||||
res.dedup_by(|a, b| a.0 == b.0);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
de.deserialize_map(IntMapVisitor::new())
|
||||
}
|
||||
|
||||
/// Take a Map with integer values and deserialize those to a Vec of sorted pairs
|
||||
///
|
||||
/// To be used like this:
|
||||
/// `#[serde(deserialize_with = "vec_deserialize_int_powerlevel_values")]`
|
||||
pub fn vec_deserialize_int_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: Deserialize<'de> + Ord,
|
||||
{
|
||||
struct IntMapVisitor<T> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> IntMapVisitor<T> {
|
||||
fn new() -> Self {
|
||||
Self { _phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
|
||||
where
|
||||
T: Deserialize<'de> + Ord,
|
||||
{
|
||||
type Value = Vec<(T, Int)>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a map with integers as values")
|
||||
}
|
||||
|
||||
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
||||
let mut res = Vec::new();
|
||||
if let Some(hint) = map.size_hint() {
|
||||
res.reserve(hint);
|
||||
}
|
||||
|
||||
while let Some(item) = map.next_entry()? {
|
||||
res.push(item);
|
||||
}
|
||||
|
||||
res.sort_unstable();
|
||||
res.dedup_by(|a, b| a.0 == b.0);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
de.deserialize_map(IntMapVisitor::new())
|
||||
}
|
||||
@@ -6,16 +6,13 @@ use std::{
|
||||
|
||||
use futures::future::ready;
|
||||
use ruma::{
|
||||
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, ServerSignatures,
|
||||
UserId, event_id,
|
||||
events::{
|
||||
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, ServerSignatures, UserId, event_id, events::{
|
||||
TimelineEventType,
|
||||
room::{
|
||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
},
|
||||
int, room_id, uint, user_id,
|
||||
}, int, room_id, room_version_rules::RoomVersionRules, uint, user_id
|
||||
};
|
||||
use serde_json::{
|
||||
json,
|
||||
@@ -24,7 +21,7 @@ use serde_json::{
|
||||
|
||||
use super::auth_types_for_event;
|
||||
use crate::{
|
||||
Result, RoomVersion, info,
|
||||
Result, info,
|
||||
matrix::{Event, EventTypeExt, Pdu, StateMap, pdu::EventHash},
|
||||
};
|
||||
|
||||
@@ -134,7 +131,7 @@ pub(crate) async fn do_check(
|
||||
let fetch = |id: OwnedEventId| ready(event_map.get(&id).cloned());
|
||||
let exists = |id: OwnedEventId| ready(event_map.get(&id).is_some());
|
||||
let resolved =
|
||||
super::resolve(&RoomVersionId::V6, state_sets, &auth_chain_sets, &fetch, &exists)
|
||||
super::resolve(&RoomVersionRules::V6, state_sets, &auth_chain_sets, &fetch, &exists)
|
||||
.await;
|
||||
|
||||
match resolved {
|
||||
@@ -154,7 +151,7 @@ pub(crate) async fn do_check(
|
||||
fake_event.sender(),
|
||||
fake_event.state_key(),
|
||||
fake_event.content(),
|
||||
&RoomVersion::V6,
|
||||
&RoomVersionRules::V6,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub fn versions() -> Vec<String> {
|
||||
vec![
|
||||
"r0.0.1".to_owned(),
|
||||
"r0.1.0".to_owned(),
|
||||
"r0.2.0".to_owned(),
|
||||
"r0.3.0".to_owned(),
|
||||
"r0.4.0".to_owned(),
|
||||
"r0.5.0".to_owned(),
|
||||
"r0.6.0".to_owned(),
|
||||
"r0.6.1".to_owned(),
|
||||
"v1.1".to_owned(),
|
||||
"v1.2".to_owned(),
|
||||
"v1.3".to_owned(),
|
||||
"v1.4".to_owned(),
|
||||
"v1.5".to_owned(),
|
||||
"v1.8".to_owned(),
|
||||
"v1.11".to_owned(),
|
||||
"v1.12".to_owned(),
|
||||
"v1.13".to_owned(),
|
||||
"v1.14".to_owned(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn unstable_features() -> BTreeMap<String, bool> {
|
||||
BTreeMap::from_iter([
|
||||
("org.matrix.e2e_cross_signing".to_owned(), true),
|
||||
("org.matrix.msc2285.stable".to_owned(), true), /* private read receipts (https://github.com/matrix-org/matrix-spec-proposals/pull/2285) */
|
||||
("uk.half-shot.msc2666.query_mutual_rooms".to_owned(), true), /* query mutual rooms (https://github.com/matrix-org/matrix-spec-proposals/pull/2666) */
|
||||
("org.matrix.msc2836".to_owned(), true), /* threading/threads (https://github.com/matrix-org/matrix-spec-proposals/pull/2836) */
|
||||
("org.matrix.msc2946".to_owned(), true), /* spaces/hierarchy summaries (https://github.com/matrix-org/matrix-spec-proposals/pull/2946) */
|
||||
("org.matrix.msc3026.busy_presence".to_owned(), true), /* busy presence status (https://github.com/matrix-org/matrix-spec-proposals/pull/3026) */
|
||||
("org.matrix.msc3827".to_owned(), true), /* filtering of /publicRooms by room type (https://github.com/matrix-org/matrix-spec-proposals/pull/3827) */
|
||||
("org.matrix.msc3952_intentional_mentions".to_owned(), true), /* intentional mentions (https://github.com/matrix-org/matrix-spec-proposals/pull/3952) */
|
||||
("org.matrix.msc3916.stable".to_owned(), true), /* authenticated media (https://github.com/matrix-org/matrix-spec-proposals/pull/3916) */
|
||||
("org.matrix.msc4180".to_owned(), true), /* stable flag for 3916 (https://github.com/matrix-org/matrix-spec-proposals/pull/4180) */
|
||||
("uk.tcpip.msc4133".to_owned(), true), /* Extending User Profile API with Key:Value Pairs (https://github.com/matrix-org/matrix-spec-proposals/pull/4133) */
|
||||
("us.cloke.msc4175".to_owned(), true), /* Profile field for user time zone (https://github.com/matrix-org/matrix-spec-proposals/pull/4175) */
|
||||
("org.matrix.simplified_msc3575".to_owned(), true), /* Simplified Sliding sync (https://github.com/matrix-org/matrix-spec-proposals/pull/4186) */
|
||||
("uk.timedout.msc4323".to_owned(), true), /* agnostic suspend (https://github.com/matrix-org/matrix-spec-proposals/pull/4323) */
|
||||
("org.matrix.msc4155".to_owned(), true), /* invite filtering (https://github.com/matrix-org/matrix-spec-proposals/pull/4155) */
|
||||
])
|
||||
}
|
||||
+13
-13
@@ -9,6 +9,18 @@ pub mod log;
|
||||
pub mod matrix;
|
||||
pub mod metrics;
|
||||
pub mod mods;
|
||||
|
||||
#[cfg(any(not(conduwuit_mods), not(feature = "conduwuit_mods")))]
|
||||
pub mod mods {
|
||||
#[macro_export]
|
||||
macro_rules! mod_ctor {
|
||||
() => {};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! mod_dtor {
|
||||
() => {};
|
||||
}
|
||||
}
|
||||
pub mod server;
|
||||
pub mod utils;
|
||||
|
||||
@@ -27,7 +39,7 @@ pub use info::{
|
||||
version::{name, version},
|
||||
};
|
||||
pub use matrix::{
|
||||
Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, RoomVersion, pdu, state_res,
|
||||
Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, pdu, state_res,
|
||||
};
|
||||
pub use parking_lot::{Mutex as SyncMutex, RwLock as SyncRwLock};
|
||||
pub use server::Server;
|
||||
@@ -36,15 +48,3 @@ pub use utils::{implement, result, result::Result};
|
||||
pub use crate as conduwuit_core;
|
||||
|
||||
conduwuit_macros::introspect_crate! {}
|
||||
|
||||
#[cfg(any(not(conduwuit_mods), not(feature = "conduwuit_mods")))]
|
||||
pub mod mods {
|
||||
#[macro_export]
|
||||
macro_rules! mod_ctor {
|
||||
() => {};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! mod_dtor {
|
||||
() => {};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user