mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-16 15:21:24 +01:00
Refactor redaction permission checks
This commit is contained in:
parent
61d2ff7183
commit
aade2e17c2
4 changed files with 83 additions and 102 deletions
|
|
@ -20,7 +20,7 @@ use serde_json::{
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::Error;
|
use crate::{utils::room_version::RoomVersion, Error};
|
||||||
|
|
||||||
/// Content hashes of a PDU.
|
/// Content hashes of a PDU.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
|
@ -104,6 +104,35 @@ impl PduEvent {
|
||||||
unsigned.redacted_because.is_some()
|
unsigned.redacted_because.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the redaction target of an event, if present.
|
||||||
|
///
|
||||||
|
/// For versions <v11, the redaction target is a top-level property. For
|
||||||
|
/// versions >=v11, it is part of the room content.
|
||||||
|
// Allowed because we don't use state_res::Event anywhere in grapevine, it's
|
||||||
|
// just implemented so we can pass Pdu to ruma stateres.
|
||||||
|
#[expect(clippy::same_name_method)]
|
||||||
|
pub(crate) fn redacts(
|
||||||
|
&self,
|
||||||
|
room_version: &RoomVersion,
|
||||||
|
) -> Option<Cow<'_, EventId>> {
|
||||||
|
if room_version.redaction_event_redacts_in_content {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct ExtractRedacts<'a> {
|
||||||
|
#[serde(borrow)]
|
||||||
|
redacts: Option<Cow<'a, EventId>>,
|
||||||
|
}
|
||||||
|
let extract =
|
||||||
|
serde_json::from_str::<ExtractRedacts<'_>>(self.content.get())
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database("Invalid content in redaction pdu.")
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
|
extract.redacts
|
||||||
|
} else {
|
||||||
|
self.redacts.as_ref().map(|e| Cow::Borrowed(&**e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn remove_transaction_id(&mut self) -> crate::Result<()> {
|
pub(crate) fn remove_transaction_id(&mut self) -> crate::Result<()> {
|
||||||
if let Some(unsigned) = &self.unsigned {
|
if let Some(unsigned) = &self.unsigned {
|
||||||
let mut unsigned: BTreeMap<String, Box<RawJsonValue>> =
|
let mut unsigned: BTreeMap<String, Box<RawJsonValue>> =
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,8 @@ use ruma::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events::{
|
events::{
|
||||||
room::{
|
room::server_acl::RoomServerAclEventContent, StateEventType,
|
||||||
redaction::RoomRedactionEventContent,
|
TimelineEventType,
|
||||||
server_acl::RoomServerAclEventContent,
|
|
||||||
},
|
|
||||||
StateEventType, TimelineEventType,
|
|
||||||
},
|
},
|
||||||
int,
|
int,
|
||||||
state_res::{self, StateMap},
|
state_res::{self, StateMap},
|
||||||
|
|
@ -43,7 +40,7 @@ use super::{
|
||||||
use crate::{
|
use crate::{
|
||||||
service::{globals::SigningKeys, pdu, rooms::state::ExtractVersion},
|
service::{globals::SigningKeys, pdu, rooms::state::ExtractVersion},
|
||||||
services,
|
services,
|
||||||
utils::{debug_slice_truncated, room_version::RoomVersion},
|
utils::debug_slice_truncated,
|
||||||
Error, PduEvent, Result,
|
Error, PduEvent, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -568,7 +565,6 @@ impl Service {
|
||||||
"Upgrading event to timeline pdu",
|
"Upgrading event to timeline pdu",
|
||||||
);
|
);
|
||||||
|
|
||||||
let room_version = RoomVersion::try_from(room_version_id)?;
|
|
||||||
let ruma_room_version = state_res::RoomVersion::new(room_version_id)
|
let ruma_room_version = state_res::RoomVersion::new(room_version_id)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::UnsupportedRoomVersion(room_version_id.clone())
|
Error::UnsupportedRoomVersion(room_version_id.clone())
|
||||||
|
|
@ -925,33 +921,10 @@ impl Service {
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.")
|
Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed.")
|
||||||
})? || incoming_pdu.kind
|
})? || incoming_pdu.kind
|
||||||
== TimelineEventType::RoomRedaction
|
== TimelineEventType::RoomRedaction
|
||||||
&& if room_version.redaction_event_redacts_in_content {
|
&& services()
|
||||||
let content =
|
.rooms
|
||||||
serde_json::from_str::<RoomRedactionEventContent>(
|
.state_accessor
|
||||||
incoming_pdu.content.get(),
|
.redaction_event_allowed(&incoming_pdu)?;
|
||||||
)
|
|
||||||
.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,
|
|
||||||
)?
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else if let Some(redact_id) = &incoming_pdu.redacts {
|
|
||||||
!services().rooms.state_accessor.user_can_redact(
|
|
||||||
redact_id,
|
|
||||||
&incoming_pdu.sender,
|
|
||||||
&incoming_pdu.room_id,
|
|
||||||
)?
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
// 13. Use state resolution to find new room state
|
// 13. Use state resolution to find new room state
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use ruma::{
|
||||||
name::RoomNameEventContent,
|
name::RoomNameEventContent,
|
||||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||||
},
|
},
|
||||||
StateEventType,
|
StateEventType, TimelineEventType,
|
||||||
},
|
},
|
||||||
state_res::Event,
|
state_res::Event,
|
||||||
EventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName,
|
EventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName,
|
||||||
|
|
@ -26,9 +26,9 @@ use tracing::{error, warn};
|
||||||
use super::short::{ShortStateHash, ShortStateKey};
|
use super::short::{ShortStateHash, ShortStateKey};
|
||||||
use crate::{
|
use crate::{
|
||||||
observability::{FoundIn, Lookup, METRICS},
|
observability::{FoundIn, Lookup, METRICS},
|
||||||
service::{globals::marker, pdu::PduBuilder},
|
service::{globals::marker, pdu::PduBuilder, rooms::state::ExtractVersion},
|
||||||
services,
|
services,
|
||||||
utils::on_demand_hashmap::KeyToken,
|
utils::{on_demand_hashmap::KeyToken, room_version::RoomVersion},
|
||||||
Error, PduEvent, Result,
|
Error, PduEvent, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -521,7 +521,7 @@ impl Service {
|
||||||
.expect("Event content always serializes");
|
.expect("Event content always serializes");
|
||||||
|
|
||||||
let new_event = PduBuilder {
|
let new_event = PduBuilder {
|
||||||
event_type: ruma::events::TimelineEventType::RoomMember,
|
event_type: TimelineEventType::RoomMember,
|
||||||
content,
|
content,
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(target_user.into()),
|
state_key: Some(target_user.into()),
|
||||||
|
|
@ -592,4 +592,31 @@ impl Service {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether a redaction event is authorized against the current state
|
||||||
|
/// of it's room.
|
||||||
|
///
|
||||||
|
/// Panics if `event` is not a `m.room.redaction` event.
|
||||||
|
pub(crate) fn redaction_event_allowed(
|
||||||
|
&self,
|
||||||
|
event: &PduEvent,
|
||||||
|
) -> Result<bool> {
|
||||||
|
assert_eq!(
|
||||||
|
event.kind,
|
||||||
|
TimelineEventType::RoomRedaction,
|
||||||
|
"event must be a redaction event"
|
||||||
|
);
|
||||||
|
|
||||||
|
let room_version_id = services()
|
||||||
|
.rooms
|
||||||
|
.state
|
||||||
|
.get_create_content::<ExtractVersion>(&event.room_id)?;
|
||||||
|
let room_version = RoomVersion::try_from(&room_version_id)?;
|
||||||
|
|
||||||
|
let Some(redacted_id) = event.redacts(&room_version) else {
|
||||||
|
return Ok(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.user_can_redact(&redacted_id, &event.sender, &event.room_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ use ruma::{
|
||||||
create::RoomCreateEventContent, encrypted::Relation,
|
create::RoomCreateEventContent, encrypted::Relation,
|
||||||
member::MembershipState, message::RoomMessageEventContent,
|
member::MembershipState, message::RoomMessageEventContent,
|
||||||
power_levels::RoomPowerLevelsEventContent,
|
power_levels::RoomPowerLevelsEventContent,
|
||||||
redaction::RoomRedactionEventContent,
|
|
||||||
},
|
},
|
||||||
StateEventType, TimelineEventType,
|
StateEventType, TimelineEventType,
|
||||||
},
|
},
|
||||||
|
|
@ -472,29 +471,14 @@ impl Service {
|
||||||
.state
|
.state
|
||||||
.get_create_content::<ExtractVersion>(&pdu.room_id)?;
|
.get_create_content::<ExtractVersion>(&pdu.room_id)?;
|
||||||
let room_version = RoomVersion::try_from(&room_version_id)?;
|
let room_version = RoomVersion::try_from(&room_version_id)?;
|
||||||
if room_version.redaction_event_redacts_in_content {
|
|
||||||
let content = serde_json::from_str::<
|
if let Some(redact_id) = pdu.redacts(&room_version) {
|
||||||
RoomRedactionEventContent,
|
if services()
|
||||||
>(pdu.content.get())
|
.rooms
|
||||||
.map_err(|_| {
|
.state_accessor
|
||||||
Error::bad_database("Invalid content in redaction pdu.")
|
.redaction_event_allowed(pdu)?
|
||||||
})?;
|
{
|
||||||
if let Some(redact_id) = &content.redacts {
|
self.redact_pdu(&redact_id, pdu, shortroomid)?;
|
||||||
if services().rooms.state_accessor.user_can_redact(
|
|
||||||
redact_id,
|
|
||||||
&pdu.sender,
|
|
||||||
&pdu.room_id,
|
|
||||||
)? {
|
|
||||||
self.redact_pdu(redact_id, pdu, shortroomid)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some(redact_id) = &pdu.redacts {
|
|
||||||
if services().rooms.state_accessor.user_can_redact(
|
|
||||||
redact_id,
|
|
||||||
&pdu.sender,
|
|
||||||
&pdu.room_id,
|
|
||||||
)? {
|
|
||||||
self.redact_pdu(redact_id, pdu, shortroomid)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1056,45 +1040,13 @@ impl Service {
|
||||||
|
|
||||||
// If redaction event is not authorized, do not append it to the
|
// If redaction event is not authorized, do not append it to the
|
||||||
// timeline
|
// timeline
|
||||||
if pdu.kind == TimelineEventType::RoomRedaction {
|
if pdu.kind == TimelineEventType::RoomRedaction
|
||||||
let room_version_id = services()
|
&& !services().rooms.state_accessor.redaction_event_allowed(&pdu)?
|
||||||
.rooms
|
{
|
||||||
.state
|
return Err(Error::BadRequest(
|
||||||
.get_create_content::<ExtractVersion>(&pdu.room_id)?;
|
ErrorKind::forbidden(),
|
||||||
let room_version = RoomVersion::try_from(&room_version_id)?;
|
"User cannot redact this event.",
|
||||||
if room_version.redaction_event_redacts_in_content {
|
));
|
||||||
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,
|
|
||||||
)? {
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"User cannot redact this event.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some(redact_id) = &pdu.redacts {
|
|
||||||
if !services().rooms.state_accessor.user_can_redact(
|
|
||||||
redact_id,
|
|
||||||
&pdu.sender,
|
|
||||||
&pdu.room_id,
|
|
||||||
)? {
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"User cannot redact this event.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We append to state before appending the pdu, so we don't have a
|
// We append to state before appending the pdu, so we don't have a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue