Merge branch 'room-v11' into 'next'
Add support for room v11 Closes #408 See merge request famedly/conduit!562
This commit is contained in:
commit
461236f3fb
8 changed files with 362 additions and 86 deletions
|
@ -23,7 +23,7 @@ use ruma::{
|
||||||
},
|
},
|
||||||
int,
|
int,
|
||||||
serde::JsonObject,
|
serde::JsonObject,
|
||||||
CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId,
|
CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId, RoomVersionId,
|
||||||
};
|
};
|
||||||
use serde_json::{json, value::to_raw_value};
|
use serde_json::{json, value::to_raw_value};
|
||||||
use std::{cmp::max, collections::BTreeMap, sync::Arc};
|
use std::{cmp::max, collections::BTreeMap, sync::Arc};
|
||||||
|
@ -127,12 +127,29 @@ pub async fn create_room_route(
|
||||||
let mut content = content
|
let mut content = content
|
||||||
.deserialize_as::<CanonicalJsonObject>()
|
.deserialize_as::<CanonicalJsonObject>()
|
||||||
.expect("Invalid creation content");
|
.expect("Invalid creation content");
|
||||||
content.insert(
|
|
||||||
"creator".into(),
|
match room_version {
|
||||||
json!(&sender_user).try_into().map_err(|_| {
|
RoomVersionId::V1
|
||||||
Error::BadRequest(ErrorKind::BadJson, "Invalid creation content")
|
| RoomVersionId::V2
|
||||||
})?,
|
| RoomVersionId::V3
|
||||||
);
|
| RoomVersionId::V4
|
||||||
|
| RoomVersionId::V5
|
||||||
|
| RoomVersionId::V6
|
||||||
|
| RoomVersionId::V7
|
||||||
|
| RoomVersionId::V8
|
||||||
|
| RoomVersionId::V9
|
||||||
|
| RoomVersionId::V10 => {
|
||||||
|
content.insert(
|
||||||
|
"creator".into(),
|
||||||
|
json!(&sender_user).try_into().map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::BadJson, "Invalid creation content")
|
||||||
|
})?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RoomVersionId::V11 => {} // V11 removed the "creator" key
|
||||||
|
_ => unreachable!("Validity of room version already checked"),
|
||||||
|
}
|
||||||
|
|
||||||
content.insert(
|
content.insert(
|
||||||
"room_version".into(),
|
"room_version".into(),
|
||||||
json!(room_version.as_str()).try_into().map_err(|_| {
|
json!(room_version.as_str()).try_into().map_err(|_| {
|
||||||
|
@ -142,9 +159,22 @@ pub async fn create_room_route(
|
||||||
content
|
content
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// TODO: Add correct value for v11
|
let content = match room_version {
|
||||||
|
RoomVersionId::V1
|
||||||
|
| RoomVersionId::V2
|
||||||
|
| RoomVersionId::V3
|
||||||
|
| RoomVersionId::V4
|
||||||
|
| RoomVersionId::V5
|
||||||
|
| RoomVersionId::V6
|
||||||
|
| RoomVersionId::V7
|
||||||
|
| RoomVersionId::V8
|
||||||
|
| RoomVersionId::V9
|
||||||
|
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(sender_user.clone()),
|
||||||
|
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
|
||||||
|
_ => unreachable!("Validity of room version already checked"),
|
||||||
|
};
|
||||||
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
||||||
to_raw_value(&RoomCreateEventContent::new_v1(sender_user.clone()))
|
to_raw_value(&content)
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?
|
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?
|
||||||
.get(),
|
.get(),
|
||||||
)
|
)
|
||||||
|
@ -619,12 +649,30 @@ pub async fn upgrade_room_route(
|
||||||
));
|
));
|
||||||
|
|
||||||
// Send a m.room.create event containing a predecessor field and the applicable room_version
|
// Send a m.room.create event containing a predecessor field and the applicable room_version
|
||||||
create_event_content.insert(
|
match body.new_version {
|
||||||
"creator".into(),
|
RoomVersionId::V1
|
||||||
json!(&sender_user)
|
| RoomVersionId::V2
|
||||||
.try_into()
|
| RoomVersionId::V3
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
| RoomVersionId::V4
|
||||||
);
|
| RoomVersionId::V5
|
||||||
|
| RoomVersionId::V6
|
||||||
|
| RoomVersionId::V7
|
||||||
|
| RoomVersionId::V8
|
||||||
|
| RoomVersionId::V9
|
||||||
|
| RoomVersionId::V10 => {
|
||||||
|
create_event_content.insert(
|
||||||
|
"creator".into(),
|
||||||
|
json!(&sender_user).try_into().map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
|
||||||
|
})?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RoomVersionId::V11 => {
|
||||||
|
// "creator" key no longer exists in V11 rooms
|
||||||
|
create_event_content.remove("creator");
|
||||||
|
}
|
||||||
|
_ => unreachable!("Validity of room version already checked"),
|
||||||
|
}
|
||||||
create_event_content.insert(
|
create_event_content.insert(
|
||||||
"room_version".into(),
|
"room_version".into(),
|
||||||
json!(&body.new_version)
|
json!(&body.new_version)
|
||||||
|
|
|
@ -965,10 +965,24 @@ impl Service {
|
||||||
|
|
||||||
services().users.create(&conduit_user, None)?;
|
services().users.create(&conduit_user, None)?;
|
||||||
|
|
||||||
let mut content = RoomCreateEventContent::new_v1(conduit_user.clone());
|
let room_version = services().globals.default_room_version();
|
||||||
|
let mut content = match room_version {
|
||||||
|
RoomVersionId::V1
|
||||||
|
| RoomVersionId::V2
|
||||||
|
| RoomVersionId::V3
|
||||||
|
| RoomVersionId::V4
|
||||||
|
| RoomVersionId::V5
|
||||||
|
| RoomVersionId::V6
|
||||||
|
| RoomVersionId::V7
|
||||||
|
| RoomVersionId::V8
|
||||||
|
| RoomVersionId::V9
|
||||||
|
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(conduit_user.clone()),
|
||||||
|
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
|
||||||
|
_ => unreachable!("Validity of room version already checked"),
|
||||||
|
};
|
||||||
content.federate = true;
|
content.federate = true;
|
||||||
content.predecessor = None;
|
content.predecessor = None;
|
||||||
content.room_version = services().globals.default_room_version();
|
content.room_version = room_version;
|
||||||
|
|
||||||
// 1. The room create event
|
// 1. The room create event
|
||||||
services()
|
services()
|
||||||
|
|
|
@ -178,6 +178,7 @@ impl Service {
|
||||||
RoomVersionId::V8,
|
RoomVersionId::V8,
|
||||||
RoomVersionId::V9,
|
RoomVersionId::V9,
|
||||||
RoomVersionId::V10,
|
RoomVersionId::V10,
|
||||||
|
RoomVersionId::V11,
|
||||||
];
|
];
|
||||||
// Experimental, partially supported room versions
|
// Experimental, partially supported room versions
|
||||||
let unstable_room_versions = vec![RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5];
|
let unstable_room_versions = vec![RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5];
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
|
canonical_json::redact_content_in_place,
|
||||||
events::{
|
events::{
|
||||||
room::member::RoomMemberEventContent, space::child::HierarchySpaceChildEvent,
|
room::{member::RoomMemberEventContent, redaction::RoomRedactionEventContent},
|
||||||
|
space::child::HierarchySpaceChildEvent,
|
||||||
AnyEphemeralRoomEvent, AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent,
|
AnyEphemeralRoomEvent, AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent,
|
||||||
AnySyncStateEvent, AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, TimelineEventType,
|
AnySyncStateEvent, AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, TimelineEventType,
|
||||||
},
|
},
|
||||||
|
@ -24,7 +26,7 @@ pub struct EventHash {
|
||||||
pub sha256: String,
|
pub sha256: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||||
pub struct PduEvent {
|
pub struct PduEvent {
|
||||||
pub event_id: Arc<EventId>,
|
pub event_id: Arc<EventId>,
|
||||||
pub room_id: OwnedRoomId,
|
pub room_id: OwnedRoomId,
|
||||||
|
@ -49,44 +51,23 @@ pub struct PduEvent {
|
||||||
|
|
||||||
impl PduEvent {
|
impl PduEvent {
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn redact(&mut self, reason: &PduEvent) -> crate::Result<()> {
|
pub fn redact(
|
||||||
|
&mut self,
|
||||||
|
room_version_id: RoomVersionId,
|
||||||
|
reason: &PduEvent,
|
||||||
|
) -> crate::Result<()> {
|
||||||
self.unsigned = None;
|
self.unsigned = None;
|
||||||
|
|
||||||
let allowed: &[&str] = match self.kind {
|
let mut content = serde_json::from_str(self.content.get())
|
||||||
TimelineEventType::RoomMember => &["join_authorised_via_users_server", "membership"],
|
.map_err(|_| Error::bad_database("PDU in db has invalid content."))?;
|
||||||
TimelineEventType::RoomCreate => &["creator"],
|
redact_content_in_place(&mut content, &room_version_id, self.kind.to_string())
|
||||||
TimelineEventType::RoomJoinRules => &["join_rule"],
|
.map_err(|e| Error::RedactionError(self.sender.server_name().to_owned(), e))?;
|
||||||
TimelineEventType::RoomPowerLevels => &[
|
|
||||||
"ban",
|
|
||||||
"events",
|
|
||||||
"events_default",
|
|
||||||
"kick",
|
|
||||||
"redact",
|
|
||||||
"state_default",
|
|
||||||
"users",
|
|
||||||
"users_default",
|
|
||||||
],
|
|
||||||
TimelineEventType::RoomHistoryVisibility => &["history_visibility"],
|
|
||||||
_ => &[],
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut old_content: BTreeMap<String, serde_json::Value> =
|
|
||||||
serde_json::from_str(self.content.get())
|
|
||||||
.map_err(|_| Error::bad_database("PDU in db has invalid content."))?;
|
|
||||||
|
|
||||||
let mut new_content = serde_json::Map::new();
|
|
||||||
|
|
||||||
for key in allowed {
|
|
||||||
if let Some(value) = old_content.remove(*key) {
|
|
||||||
new_content.insert((*key).to_owned(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.unsigned = Some(to_raw_value(&json!({
|
self.unsigned = Some(to_raw_value(&json!({
|
||||||
"redacted_because": serde_json::to_value(reason).expect("to_value(PduEvent) always works")
|
"redacted_because": serde_json::to_value(reason).expect("to_value(PduEvent) always works")
|
||||||
})).expect("to string always works"));
|
})).expect("to string always works"));
|
||||||
|
|
||||||
self.content = to_raw_value(&new_content).expect("to string always works");
|
self.content = to_raw_value(&content).expect("to string always works");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -116,10 +97,43 @@ impl PduEvent {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copies the `redacts` property of the event to the `content` dict and vice-versa.
|
||||||
|
///
|
||||||
|
/// This follows the specification's
|
||||||
|
/// [recommendation](https://spec.matrix.org/v1.10/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property):
|
||||||
|
///
|
||||||
|
/// > For backwards-compatibility with older clients, servers should add a redacts
|
||||||
|
/// > property to the top level of m.room.redaction events in when serving such events
|
||||||
|
/// > over the Client-Server API.
|
||||||
|
/// >
|
||||||
|
/// > For improved compatibility with newer clients, servers should add a redacts property
|
||||||
|
/// > to the content of m.room.redaction events in older room versions when serving
|
||||||
|
/// > such events over the Client-Server API.
|
||||||
|
pub fn copy_redacts(&self) -> (Option<Arc<EventId>>, Box<RawJsonValue>) {
|
||||||
|
if self.kind == TimelineEventType::RoomRedaction {
|
||||||
|
if let Ok(mut content) =
|
||||||
|
serde_json::from_str::<RoomRedactionEventContent>(self.content.get())
|
||||||
|
{
|
||||||
|
if let Some(redacts) = content.redacts {
|
||||||
|
return (Some(redacts.into()), self.content.clone());
|
||||||
|
} else if let Some(redacts) = self.redacts.clone() {
|
||||||
|
content.redacts = Some(redacts.into());
|
||||||
|
return (
|
||||||
|
self.redacts.clone(),
|
||||||
|
to_raw_value(&content).expect("Must be valid, we only added redacts field"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(self.redacts.clone(), self.content.clone())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn to_sync_room_event(&self) -> Raw<AnySyncTimelineEvent> {
|
pub fn to_sync_room_event(&self) -> Raw<AnySyncTimelineEvent> {
|
||||||
|
let (redacts, content) = self.copy_redacts();
|
||||||
let mut json = json!({
|
let mut json = json!({
|
||||||
"content": self.content,
|
"content": content,
|
||||||
"type": self.kind,
|
"type": self.kind,
|
||||||
"event_id": self.event_id,
|
"event_id": self.event_id,
|
||||||
"sender": self.sender,
|
"sender": self.sender,
|
||||||
|
@ -132,7 +146,7 @@ impl PduEvent {
|
||||||
if let Some(state_key) = &self.state_key {
|
if let Some(state_key) = &self.state_key {
|
||||||
json["state_key"] = json!(state_key);
|
json["state_key"] = json!(state_key);
|
||||||
}
|
}
|
||||||
if let Some(redacts) = &self.redacts {
|
if let Some(redacts) = &redacts {
|
||||||
json["redacts"] = json!(redacts);
|
json["redacts"] = json!(redacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,8 +180,9 @@ impl PduEvent {
|
||||||
|
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn to_room_event(&self) -> Raw<AnyTimelineEvent> {
|
pub fn to_room_event(&self) -> Raw<AnyTimelineEvent> {
|
||||||
|
let (redacts, content) = self.copy_redacts();
|
||||||
let mut json = json!({
|
let mut json = json!({
|
||||||
"content": self.content,
|
"content": content,
|
||||||
"type": self.kind,
|
"type": self.kind,
|
||||||
"event_id": self.event_id,
|
"event_id": self.event_id,
|
||||||
"sender": self.sender,
|
"sender": self.sender,
|
||||||
|
@ -181,7 +196,7 @@ impl PduEvent {
|
||||||
if let Some(state_key) = &self.state_key {
|
if let Some(state_key) = &self.state_key {
|
||||||
json["state_key"] = json!(state_key);
|
json["state_key"] = json!(state_key);
|
||||||
}
|
}
|
||||||
if let Some(redacts) = &self.redacts {
|
if let Some(redacts) = &redacts {
|
||||||
json["redacts"] = json!(redacts);
|
json["redacts"] = json!(redacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,8 +205,9 @@ impl PduEvent {
|
||||||
|
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn to_message_like_event(&self) -> Raw<AnyMessageLikeEvent> {
|
pub fn to_message_like_event(&self) -> Raw<AnyMessageLikeEvent> {
|
||||||
|
let (redacts, content) = self.copy_redacts();
|
||||||
let mut json = json!({
|
let mut json = json!({
|
||||||
"content": self.content,
|
"content": content,
|
||||||
"type": self.kind,
|
"type": self.kind,
|
||||||
"event_id": self.event_id,
|
"event_id": self.event_id,
|
||||||
"sender": self.sender,
|
"sender": self.sender,
|
||||||
|
@ -205,7 +221,7 @@ impl PduEvent {
|
||||||
if let Some(state_key) = &self.state_key {
|
if let Some(state_key) = &self.state_key {
|
||||||
json["state_key"] = json!(state_key);
|
json["state_key"] = json!(state_key);
|
||||||
}
|
}
|
||||||
if let Some(redacts) = &self.redacts {
|
if let Some(redacts) = &redacts {
|
||||||
json["redacts"] = json!(redacts);
|
json["redacts"] = json!(redacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,11 @@ use ruma::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events::{
|
events::{
|
||||||
room::{create::RoomCreateEventContent, server_acl::RoomServerAclEventContent},
|
room::{
|
||||||
StateEventType,
|
create::RoomCreateEventContent, redaction::RoomRedactionEventContent,
|
||||||
|
server_acl::RoomServerAclEventContent,
|
||||||
|
},
|
||||||
|
StateEventType, TimelineEventType,
|
||||||
},
|
},
|
||||||
int,
|
int,
|
||||||
serde::Base64,
|
serde::Base64,
|
||||||
|
@ -796,7 +799,51 @@ impl Service {
|
||||||
None::<PduEvent>,
|
None::<PduEvent>,
|
||||||
|k, s| auth_events.get(&(k.clone(), s.to_owned())),
|
|k, s| auth_events.get(&(k.clone(), s.to_owned())),
|
||||||
)
|
)
|
||||||
.map_err(|_e| Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed."))?;
|
.map_err(|_e| Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed."))?
|
||||||
|
|| incoming_pdu.kind == TimelineEventType::RoomRedaction
|
||||||
|
&& match room_version_id {
|
||||||
|
RoomVersionId::V1
|
||||||
|
| RoomVersionId::V2
|
||||||
|
| RoomVersionId::V3
|
||||||
|
| RoomVersionId::V4
|
||||||
|
| RoomVersionId::V5
|
||||||
|
| RoomVersionId::V6
|
||||||
|
| RoomVersionId::V7
|
||||||
|
| RoomVersionId::V8
|
||||||
|
| RoomVersionId::V9
|
||||||
|
| RoomVersionId::V10 => {
|
||||||
|
if let Some(redact_id) = &incoming_pdu.redacts {
|
||||||
|
!services().rooms.state_accessor.user_can_redact(
|
||||||
|
redact_id,
|
||||||
|
&incoming_pdu.sender,
|
||||||
|
&incoming_pdu.room_id,
|
||||||
|
true,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoomVersionId::V11 => {
|
||||||
|
let content = serde_json::from_str::<RoomRedactionEventContent>(
|
||||||
|
incoming_pdu.content.get(),
|
||||||
|
)
|
||||||
|
.map_err(|_| Error::bad_database("Invalid content in redaction pdu."))?;
|
||||||
|
|
||||||
|
if let Some(redact_id) = &content.redacts {
|
||||||
|
!services().rooms.state_accessor.user_can_redact(
|
||||||
|
redact_id,
|
||||||
|
&incoming_pdu.sender,
|
||||||
|
&incoming_pdu.room_id,
|
||||||
|
true,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("Validity of room version already checked")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 13. Use state resolution to find new room state
|
// 13. Use state resolution to find new room state
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,11 @@ use ruma::{
|
||||||
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
||||||
member::{MembershipState, RoomMemberEventContent},
|
member::{MembershipState, RoomMemberEventContent},
|
||||||
name::RoomNameEventContent,
|
name::RoomNameEventContent,
|
||||||
|
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||||
},
|
},
|
||||||
StateEventType,
|
StateEventType,
|
||||||
},
|
},
|
||||||
|
state_res::Event,
|
||||||
EventId, JsOption, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
EventId, JsOption, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
use serde_json::value::to_raw_value;
|
use serde_json::value::to_raw_value;
|
||||||
|
@ -351,4 +353,55 @@ impl Service {
|
||||||
.map_err(|_| Error::bad_database("Invalid room member event in database."))
|
.map_err(|_| Error::bad_database("Invalid room member event in database."))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a given user can redact a given event
|
||||||
|
///
|
||||||
|
/// If `federation` is `true`, it allows redaction events from any user of the same server
|
||||||
|
/// as the original event sender, [as required by room versions >=
|
||||||
|
/// v3](https://spec.matrix.org/v1.10/rooms/v11/#handling-redactions)
|
||||||
|
pub fn user_can_redact(
|
||||||
|
&self,
|
||||||
|
redacts: &EventId,
|
||||||
|
sender: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
federation: bool,
|
||||||
|
) -> Result<bool> {
|
||||||
|
self.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
||||||
|
.map(|e| {
|
||||||
|
serde_json::from_str(e.content.get())
|
||||||
|
.map(|c: RoomPowerLevelsEventContent| c.into())
|
||||||
|
.map(|e: RoomPowerLevels| {
|
||||||
|
e.user_can_redact_event_of_other(sender)
|
||||||
|
|| e.user_can_redact_own_event(sender)
|
||||||
|
&& if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts)
|
||||||
|
{
|
||||||
|
if federation {
|
||||||
|
pdu.sender().server_name() == sender.server_name()
|
||||||
|
} else {
|
||||||
|
pdu.sender == sender
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database("Invalid m.room.power_levels event in database")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// Falling back on m.room.create to judge power levels
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
if let Some(pdu) = self.room_state_get(room_id, &StateEventType::RoomCreate, "")? {
|
||||||
|
Ok(pdu.sender == sender
|
||||||
|
|| if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) {
|
||||||
|
pdu.sender == sender
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::bad_database(
|
||||||
|
"No m.room.power_levels or m.room.create events in database for room",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use ruma::{
|
||||||
push_rules::PushRulesEvent,
|
push_rules::PushRulesEvent,
|
||||||
room::{
|
room::{
|
||||||
create::RoomCreateEventContent, encrypted::Relation, member::MembershipState,
|
create::RoomCreateEventContent, encrypted::Relation, member::MembershipState,
|
||||||
power_levels::RoomPowerLevelsEventContent,
|
power_levels::RoomPowerLevelsEventContent, redaction::RoomRedactionEventContent,
|
||||||
},
|
},
|
||||||
GlobalAccountDataEventType, StateEventType, TimelineEventType,
|
GlobalAccountDataEventType, StateEventType, TimelineEventType,
|
||||||
},
|
},
|
||||||
|
@ -23,7 +23,7 @@ use ruma::{
|
||||||
serde::Base64,
|
serde::Base64,
|
||||||
state_res::{self, Event, RoomVersion},
|
state_res::{self, Event, RoomVersion},
|
||||||
uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
|
uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
|
||||||
OwnedServerName, RoomId, ServerName, UserId,
|
OwnedServerName, RoomId, RoomVersionId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||||
|
@ -382,9 +382,48 @@ impl Service {
|
||||||
|
|
||||||
match pdu.kind {
|
match pdu.kind {
|
||||||
TimelineEventType::RoomRedaction => {
|
TimelineEventType::RoomRedaction => {
|
||||||
if let Some(redact_id) = &pdu.redacts {
|
let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?;
|
||||||
self.redact_pdu(redact_id, pdu)?;
|
match room_version_id {
|
||||||
}
|
RoomVersionId::V1
|
||||||
|
| RoomVersionId::V2
|
||||||
|
| RoomVersionId::V3
|
||||||
|
| RoomVersionId::V4
|
||||||
|
| RoomVersionId::V5
|
||||||
|
| RoomVersionId::V6
|
||||||
|
| RoomVersionId::V7
|
||||||
|
| RoomVersionId::V8
|
||||||
|
| RoomVersionId::V9
|
||||||
|
| RoomVersionId::V10 => {
|
||||||
|
if let Some(redact_id) = &pdu.redacts {
|
||||||
|
if services().rooms.state_accessor.user_can_redact(
|
||||||
|
redact_id,
|
||||||
|
&pdu.sender,
|
||||||
|
&pdu.room_id,
|
||||||
|
false,
|
||||||
|
)? {
|
||||||
|
self.redact_pdu(redact_id, pdu)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoomVersionId::V11 => {
|
||||||
|
let content =
|
||||||
|
serde_json::from_str::<RoomRedactionEventContent>(pdu.content.get())
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database("Invalid content in redaction pdu.")
|
||||||
|
})?;
|
||||||
|
if let Some(redact_id) = &content.redacts {
|
||||||
|
if services().rooms.state_accessor.user_can_redact(
|
||||||
|
redact_id,
|
||||||
|
&pdu.sender,
|
||||||
|
&pdu.room_id,
|
||||||
|
false,
|
||||||
|
)? {
|
||||||
|
self.redact_pdu(redact_id, pdu)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("Validity of room version already checked"),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
TimelineEventType::SpaceChild => {
|
TimelineEventType::SpaceChild => {
|
||||||
if let Some(_state_key) = &pdu.state_key {
|
if let Some(_state_key) = &pdu.state_key {
|
||||||
|
@ -608,28 +647,24 @@ impl Service {
|
||||||
.take(20)
|
.take(20)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let create_event = services().rooms.state_accessor.room_state_get(
|
// If there was no create event yet, assume we are creating a room
|
||||||
room_id,
|
let room_version_id = services()
|
||||||
&StateEventType::RoomCreate,
|
.rooms
|
||||||
"",
|
.state
|
||||||
)?;
|
.get_room_version(room_id)
|
||||||
|
.or_else(|_| {
|
||||||
|
if event_type == TimelineEventType::RoomCreate {
|
||||||
|
let content = serde_json::from_str::<RoomCreateEventContent>(content.get())
|
||||||
|
.expect("Invalid content in RoomCreate pdu.");
|
||||||
|
Ok(content.room_version)
|
||||||
|
} else {
|
||||||
|
Err(Error::InconsistentRoomState(
|
||||||
|
"non-create event for room of unknown version",
|
||||||
|
room_id.to_owned(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
let create_event_content: Option<RoomCreateEventContent> = create_event
|
|
||||||
.as_ref()
|
|
||||||
.map(|create_event| {
|
|
||||||
serde_json::from_str(create_event.content.get()).map_err(|e| {
|
|
||||||
warn!("Invalid create event: {}", e);
|
|
||||||
Error::bad_database("Invalid create event in db.")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
// If there was no create event yet, assume we are creating a room with the default
|
|
||||||
// version right now
|
|
||||||
let room_version_id = create_event_content
|
|
||||||
.map_or(services().globals.default_room_version(), |create_event| {
|
|
||||||
create_event.room_version
|
|
||||||
});
|
|
||||||
let room_version = RoomVersion::new(&room_version_id).expect("room version is supported");
|
let room_version = RoomVersion::new(&room_version_id).expect("room version is supported");
|
||||||
|
|
||||||
let auth_events = services().rooms.state.get_auth_events(
|
let auth_events = services().rooms.state.get_auth_events(
|
||||||
|
@ -864,6 +899,63 @@ impl Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If redaction event is not authorized, do not append it to the timeline
|
||||||
|
if pdu.kind == TimelineEventType::RoomRedaction {
|
||||||
|
match services().rooms.state.get_room_version(&pdu.room_id)? {
|
||||||
|
RoomVersionId::V1
|
||||||
|
| RoomVersionId::V2
|
||||||
|
| RoomVersionId::V3
|
||||||
|
| RoomVersionId::V4
|
||||||
|
| RoomVersionId::V5
|
||||||
|
| RoomVersionId::V6
|
||||||
|
| RoomVersionId::V7
|
||||||
|
| RoomVersionId::V8
|
||||||
|
| RoomVersionId::V9
|
||||||
|
| RoomVersionId::V10 => {
|
||||||
|
if let Some(redact_id) = &pdu.redacts {
|
||||||
|
if !services().rooms.state_accessor.user_can_redact(
|
||||||
|
redact_id,
|
||||||
|
&pdu.sender,
|
||||||
|
&pdu.room_id,
|
||||||
|
false,
|
||||||
|
)? {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Forbidden,
|
||||||
|
"User cannot redact this event.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
RoomVersionId::V11 => {
|
||||||
|
let content =
|
||||||
|
serde_json::from_str::<RoomRedactionEventContent>(pdu.content.get())
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database("Invalid content in redaction pdu.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let Some(redact_id) = &content.redacts {
|
||||||
|
if !services().rooms.state_accessor.user_can_redact(
|
||||||
|
redact_id,
|
||||||
|
&pdu.sender,
|
||||||
|
&pdu.room_id,
|
||||||
|
false,
|
||||||
|
)? {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Forbidden,
|
||||||
|
"User cannot redact this event.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::UnsupportedRoomVersion,
|
||||||
|
"Unsupported room version",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We append to state before appending the pdu, so we don't have a moment in time with the
|
// We append to state before appending the pdu, so we don't have a moment in time with the
|
||||||
// pdu without it's state. This is okay because append_pdu can't fail.
|
// pdu without it's state. This is okay because append_pdu can't fail.
|
||||||
let statehashid = services().rooms.state.append_to_state(&pdu)?;
|
let statehashid = services().rooms.state.append_to_state(&pdu)?;
|
||||||
|
@ -995,7 +1087,8 @@ impl Service {
|
||||||
let mut pdu = self
|
let mut pdu = self
|
||||||
.get_pdu_from_id(&pdu_id)?
|
.get_pdu_from_id(&pdu_id)?
|
||||||
.ok_or_else(|| Error::bad_database("PDU ID points to invalid PDU."))?;
|
.ok_or_else(|| Error::bad_database("PDU ID points to invalid PDU."))?;
|
||||||
pdu.redact(reason)?;
|
let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?;
|
||||||
|
pdu.redact(room_version_id, reason)?;
|
||||||
self.replace_pdu(
|
self.replace_pdu(
|
||||||
&pdu_id,
|
&pdu_id,
|
||||||
&utils::to_canonical_object(&pdu).expect("PDU is an object"),
|
&utils::to_canonical_object(&pdu).expect("PDU is an object"),
|
||||||
|
|
|
@ -87,6 +87,10 @@ pub enum Error {
|
||||||
PathError(#[from] axum::extract::rejection::PathRejection),
|
PathError(#[from] axum::extract::rejection::PathRejection),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
AdminCommand(&'static str),
|
AdminCommand(&'static str),
|
||||||
|
#[error("from {0}: {1}")]
|
||||||
|
RedactionError(OwnedServerName, ruma::canonical_json::RedactionError),
|
||||||
|
#[error("{0} in {1}")]
|
||||||
|
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
|
Loading…
Add table
Reference in a new issue