feat: redaction

This commit is contained in:
timokoesters 2020-05-26 10:27:51 +02:00
parent 16538a6c16
commit 18bf67748c
No known key found for this signature in database
GPG key ID: 356E705610F626D5
6 changed files with 204 additions and 107 deletions

View file

@ -39,13 +39,13 @@ use ruma_client_api::{
to_device::{self, send_event_to_device},
typing::create_typing_event,
uiaa::{AuthFlow, UiaaInfo, UiaaResponse},
user_directory::search_users,
user_directory::search_users, redact::redact_event,
},
unversioned::get_supported_versions,
};
use ruma_events::{
collections::only::Event as EduEvent,
room::{guest_access, history_visibility, join_rules, member},
room::{guest_access, history_visibility, join_rules, member, redaction},
EventJson, EventType,
};
use ruma_identifiers::{RoomId, RoomVersionId, UserId};
@ -517,6 +517,7 @@ pub fn set_displayname_route(
.unwrap(),
None,
Some(user_id.to_string()),
None,
&db.globals,
)
.unwrap();
@ -564,7 +565,7 @@ pub fn set_avatar_url_route(
) -> MatrixResult<set_avatar_url::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
if let avatar_url = &body.avatar_url {
if let Some(avatar_url) = &body.avatar_url {
if !avatar_url.starts_with("mxc://") {
debug!("Request contains an invalid avatar_url.");
return MatrixResult(Err(Error {
@ -579,7 +580,7 @@ pub fn set_avatar_url_route(
}
db.users
.set_avatar_url(&user_id, Some(body.avatar_url.clone()))
.set_avatar_url(&user_id, body.avatar_url.clone())
.unwrap();
// Send a new membership event into all joined rooms
@ -591,7 +592,7 @@ pub fn set_avatar_url_route(
user_id.clone(),
EventType::RoomMember,
serde_json::to_value(ruma_events::room::member::MemberEventContent {
avatar_url: Some(body.avatar_url.clone()),
avatar_url: body.avatar_url.clone(),
..serde_json::from_value::<EventJson<_>>(
db.rooms
.room_state(&room_id)
@ -608,6 +609,7 @@ pub fn set_avatar_url_route(
.unwrap(),
None,
Some(user_id.to_string()),
None,
&db.globals,
)
.unwrap();
@ -917,14 +919,15 @@ pub fn create_room_route(
creator: user_id.clone(),
federate: body
.creation_content
.and_then(|c| c.federate)
.unwrap_or(true),
predecessor: None, // TODO: Check creation_content.predecessor once ruma has that
.as_ref()
.map_or(true, |c| c.federate),
predecessor: body.creation_content.as_ref().and_then(|c| c.predecessor.clone()),
room_version: RoomVersionId::version_5(),
})
.unwrap(),
None,
Some("".to_owned()),
None,
&db.globals,
)
.unwrap();
@ -945,6 +948,7 @@ pub fn create_room_route(
.unwrap(),
None,
Some(user_id.to_string()),
None,
&db.globals,
)
.unwrap();
@ -968,18 +972,8 @@ pub fn create_room_route(
.expect("TODO: handle. we hope the client sends a valid power levels json")
} else {
serde_json::to_value(ruma_events::room::power_levels::PowerLevelsEventContent {
ban: 50.into(),
events: BTreeMap::new(),
events_default: 0.into(),
invite: 50.into(),
kick: 50.into(),
redact: 50.into(),
state_default: 50.into(),
users,
users_default: 0.into(),
notifications: ruma_events::room::power_levels::NotificationPowerLevels {
room: 50.into(),
},
..Default::default()
})
.unwrap()
};
@ -991,6 +985,7 @@ pub fn create_room_route(
power_levels_content,
None,
Some("".to_owned()),
None,
&db.globals,
)
.unwrap();
@ -1016,6 +1011,7 @@ pub fn create_room_route(
},
None,
Some("".to_owned()),
None,
&db.globals,
)
.unwrap();
@ -1032,6 +1028,7 @@ pub fn create_room_route(
.unwrap(),
None,
Some("".to_owned()),
None,
&db.globals,
)
.unwrap();
@ -1056,6 +1053,7 @@ pub fn create_room_route(
},
None,
Some("".to_owned()),
None,
&db.globals,
)
.unwrap();
@ -1071,10 +1069,11 @@ pub fn create_room_route(
.append_pdu(
room_id.clone(),
user_id.clone(),
EventType::from(event_type),
event_type.clone(),
serde_json::from_str(content.get()).unwrap(),
None,
state_key.clone(),
None,
&db.globals,
)
.unwrap();
@ -1093,6 +1092,7 @@ pub fn create_room_route(
.unwrap(),
None,
Some("".to_owned()),
None,
&db.globals,
)
.unwrap();
@ -1110,6 +1110,7 @@ pub fn create_room_route(
.unwrap(),
None,
Some("".to_owned()),
None,
&db.globals,
)
.unwrap();
@ -1132,6 +1133,7 @@ pub fn create_room_route(
.unwrap(),
None,
Some(user.to_string()),
None,
&db.globals,
)
.unwrap();
@ -1140,6 +1142,38 @@ pub fn create_room_route(
MatrixResult(Ok(create_room::Response { room_id }))
}
#[put("/_matrix/client/r0/rooms/<_room_id>/redact/<_event_id>/<_txn_id>", data = "<body>")]
pub fn redact_event_route(
db: State<'_, Database>,
body: Ruma<redact_event::Request>,
_room_id: String,
_event_id: String,
_txn_id: String,
) -> MatrixResult<redact_event::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
if let Ok(event_id) = db.rooms.append_pdu(
body.room_id.clone(),
user_id.clone(),
EventType::RoomRedaction,
serde_json::to_value(redaction::RedactionEventContent {
reason: body.reason.clone(),
}).unwrap(),
None,
None,
Some(body.event_id.clone()),
&db.globals,
) {
MatrixResult(Ok(redact_event::Response { event_id }))
} else {
MatrixResult(Err(Error {
kind: ErrorKind::Unknown,
message: "Failed to redact event.".to_owned(),
status_code: http::StatusCode::BAD_REQUEST,
}))
}
}
#[get("/_matrix/client/r0/directory/room/<_room_alias>", data = "<body>")]
pub fn get_alias_route(
db: State<'_, Database>,
@ -1220,6 +1254,7 @@ pub fn join_room_by_id_route(
serde_json::to_value(event).unwrap(),
None,
Some(user_id.to_string()),
None,
&db.globals,
)
.unwrap();
@ -1285,6 +1320,7 @@ pub fn leave_room_route(
json!({"membership": "leave"}),
None,
Some(user_id.to_string()),
None,
&db.globals,
)
.unwrap();
@ -1328,6 +1364,7 @@ pub fn invite_user_route(
serde_json::to_value(event).unwrap(),
None,
Some(user_id.to_string()),
None,
&db.globals,
)
.unwrap();
@ -1414,7 +1451,7 @@ pub async fn get_public_rooms_filtered_route(
.deserialize()
.unwrap()
.alias
}).map(|a| a.to_string()),
}),
name: state.get(&(EventType::RoomName, "".to_owned())).map(|s| {
serde_json::from_value::<EventJson<ruma_events::room::name::NameEventContent>>(
s.content.clone(),
@ -1565,6 +1602,7 @@ pub fn create_message_event_route(
serde_json::from_str(body.json_body.unwrap().get()).unwrap(),
Some(unsigned),
None,
None,
&db.globals,
) {
MatrixResult(Ok(create_message_event::Response { event_id }))
@ -1598,6 +1636,7 @@ pub fn create_state_event_for_key_route(
serde_json::from_str(body.json_body.clone().unwrap().get()).unwrap(),
None,
Some(body.state_key.clone()),
None,
&db.globals,
) {
MatrixResult(Ok(create_state_event_for_key::Response { event_id }))
@ -1630,6 +1669,7 @@ pub fn create_state_event_for_empty_key_route(
serde_json::from_str(body.json_body.unwrap().get()).unwrap(),
None,
Some("".to_owned()),
None,
&db.globals,
) {
MatrixResult(Ok(create_state_event_for_empty_key::Response { event_id }))

View file

@ -12,7 +12,7 @@ use ruma_events::{
EventJson, EventType,
};
use ruma_identifiers::{EventId, RoomId, UserId};
use sled::IVec;
use std::{
collections::{BTreeMap, HashMap},
convert::{TryFrom, TryInto},
@ -110,28 +110,50 @@ impl Rooms {
self.eventid_pduid
.get(event_id.to_string().as_bytes())?
.map_or(Ok(None), |pdu_id| {
Ok(serde_json::from_slice(
Ok(Some(serde_json::from_slice(
&self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase(
"eventid_pduid points to nonexistent pdu",
))?,
)?)
.map(Some)
)?))
})
}
/// Returns the pdu's id.
pub fn get_pdu_id(&self, event_id: &EventId) -> Result<Option<IVec>> {
self.eventid_pduid
.get(event_id.to_string().as_bytes())?
.map_or(Ok(None), |pdu_id| Ok(Some(pdu_id)))
}
/// Returns the pdu.
pub fn get_pdu(&self, event_id: &EventId) -> Result<Option<PduEvent>> {
self.eventid_pduid
.get(event_id.to_string().as_bytes())?
.map_or(Ok(None), |pdu_id| {
Ok(serde_json::from_slice(
Ok(Some(serde_json::from_slice(
&self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase(
"eventid_pduid points to nonexistent pdu",
))?,
)?)
.map(Some)
)?))
})
}
/// Returns the pdu.
pub fn get_pdu_from_id(&self, pdu_id: &IVec) -> Result<Option<PduEvent>> {
self.pduid_pdu
.get(pdu_id)?
.map_or(Ok(None), |pdu| Ok(Some(serde_json::from_slice(&pdu)?)))
}
/// Returns the pdu.
pub fn replace_pdu(&self, pdu_id: &IVec, pdu: &PduEvent) -> Result<()> {
if self.pduid_pdu.get(&pdu_id)?.is_some() {
self.pduid_pdu
.insert(&pdu_id, &*serde_json::to_string(pdu)?)?;
Ok(())
} else {
Err(Error::BadRequest("pdu does not exist"))
}
}
/// Returns the leaf pdus of a room.
pub fn get_pdu_leaves(&self, room_id: &RoomId) -> Result<Vec<EventId>> {
@ -177,6 +199,7 @@ impl Rooms {
content: serde_json::Value,
unsigned: Option<serde_json::Map<String, serde_json::Value>>,
state_key: Option<String>,
redacts: Option<EventId>,
globals: &super::globals::Globals,
) -> Result<EventId> {
// TODO: Make sure this isn't called twice in parallel
@ -435,7 +458,7 @@ impl Rooms {
.try_into()
.expect("depth can overflow and should be deprecated..."),
auth_events: Vec::new(),
redacts: None,
redacts,
unsigned,
hashes: ruma_federation_api::EventHash {
sha256: "aaa".to_owned(),
@ -555,7 +578,21 @@ impl Rooms {
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))
}
/// Makes a user join a room. Only call this if the membership is Join already
/// Replace a PDU with the redacted form.
pub fn redact_pdu(&self, event_id: &EventId) -> Result<()> {
if let Some(pdu_id) = self.get_pdu_id(event_id)? {
let mut pdu = self
.get_pdu_from_id(&pdu_id)?
.ok_or(Error::BadDatabase("pduid points to invalid pdu"))?;
pdu.redact();
self.replace_pdu(&pdu_id, &pdu)?;
Ok(())
} else {
Err(Error::BadRequest("eventid does not exist"))
}
}
/// Update current membership data.
fn update_membership(
&self,
room_id: &RoomId,

View file

@ -33,6 +33,38 @@ pub struct PduEvent {
}
impl PduEvent {
pub fn redact(&mut self) {
self.unsigned.clear();
let allowed = match self.kind {
EventType::RoomMember => vec!["membership"],
EventType::RoomCreate => vec!["creator"],
EventType::RoomJoinRules => vec!["join_rule"],
EventType::RoomPowerLevels => vec![
"ban",
"events",
"events_default",
"kick",
"redact",
"state_default",
"users",
"users_default",
],
EventType::RoomHistoryVisibility => vec!["history_visibility"],
_ => vec![],
};
let old_content = self.content.as_object_mut().unwrap(); // TODO error
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.content = new_content.into();
}
pub fn to_room_event(&self) -> EventJson<RoomEvent> {
// Can only fail in rare circumstances that won't ever happen here, see
// https://docs.rs/serde_json/1.0.50/serde_json/fn.to_string.html
@ -40,7 +72,6 @@ impl PduEvent {
// EventJson's deserialize implementation always returns `Ok(...)`
serde_json::from_str::<EventJson<RoomEvent>>(&json).unwrap()
}
pub fn to_state_event(&self) -> EventJson<StateEvent> {
let json = serde_json::to_string(&self).unwrap();
serde_json::from_str::<EventJson<StateEvent>>(&json).unwrap()

View file

@ -4,7 +4,9 @@ use log::error;
use rocket::{get, response::content::Json, State};
use ruma_api::Endpoint;
use ruma_client_api::error::Error;
use ruma_federation_api::{v1::get_server_version, v2::get_server_keys};
use ruma_federation_api::discovery::{
get_server_keys::v2 as get_server_keys, get_server_version::v1 as get_server_version,
};
use serde_json::json;
use std::{
collections::BTreeMap,