improvement: federation get_keys and optimize signingkey storage
- get encryption keys over federation - optimize signing key storage - rate limit parsing of bad events - rate limit signature fetching - dependency bumps
This commit is contained in:
parent
ae41bc5067
commit
09157b2096
18 changed files with 566 additions and 371 deletions
|
@ -8,11 +8,11 @@ use ruma::{
|
|||
set_room_account_data,
|
||||
},
|
||||
},
|
||||
events::{custom::CustomEventContent, AnyBasicEventContent, BasicEvent},
|
||||
events::{AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent},
|
||||
serde::Raw,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
use serde_json::{json, value::RawValue as RawJsonValue};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::{get, put};
|
||||
|
@ -28,7 +28,7 @@ pub async fn set_global_account_data_route(
|
|||
) -> ConduitResult<set_global_account_data::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let data = serde_json::from_str(body.data.get())
|
||||
let data = serde_json::from_str::<serde_json::Value>(body.data.get())
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
|
||||
|
||||
let event_type = body.event_type.to_string();
|
||||
|
@ -37,9 +37,10 @@ pub async fn set_global_account_data_route(
|
|||
None,
|
||||
sender_user,
|
||||
event_type.clone().into(),
|
||||
&BasicEvent {
|
||||
content: CustomEventContent { event_type, data },
|
||||
},
|
||||
&json!({
|
||||
"type": event_type,
|
||||
"content": data,
|
||||
}),
|
||||
&db.globals,
|
||||
)?;
|
||||
|
||||
|
@ -71,9 +72,10 @@ pub async fn set_room_account_data_route(
|
|||
Some(&body.room_id),
|
||||
sender_user,
|
||||
event_type.clone().into(),
|
||||
&BasicEvent {
|
||||
content: CustomEventContent { event_type, data },
|
||||
},
|
||||
&json!({
|
||||
"type": event_type,
|
||||
"content": data,
|
||||
}),
|
||||
&db.globals,
|
||||
)?;
|
||||
|
||||
|
@ -99,7 +101,7 @@ pub async fn get_global_account_data_route(
|
|||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||
db.flush().await?;
|
||||
|
||||
let account_data = serde_json::from_str::<ExtractEventContent>(event.get())
|
||||
let account_data = serde_json::from_str::<ExtractGlobalEventContent>(event.get())
|
||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
|
||||
.content;
|
||||
|
||||
|
@ -130,7 +132,7 @@ pub async fn get_room_account_data_route(
|
|||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||
db.flush().await?;
|
||||
|
||||
let account_data = serde_json::from_str::<ExtractEventContent>(event.get())
|
||||
let account_data = serde_json::from_str::<ExtractRoomEventContent>(event.get())
|
||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
|
||||
.content;
|
||||
|
||||
|
@ -138,6 +140,11 @@ pub async fn get_room_account_data_route(
|
|||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ExtractEventContent {
|
||||
content: Raw<AnyBasicEventContent>,
|
||||
struct ExtractRoomEventContent {
|
||||
content: Raw<AnyRoomAccountDataEventContent>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ExtractGlobalEventContent {
|
||||
content: Raw<AnyGlobalAccountDataEventContent>,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::{State, SESSION_ID_LENGTH};
|
||||
use crate::{utils, ConduitResult, Database, Error, Ruma};
|
||||
use crate::{utils, ConduitResult, Database, Error, Result, Ruma};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
|
@ -12,6 +12,7 @@ use ruma::{
|
|||
},
|
||||
},
|
||||
encryption::UnsignedDeviceInfo,
|
||||
DeviceId, UserId,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
|
@ -78,74 +79,14 @@ pub async fn get_keys_route(
|
|||
) -> ConduitResult<get_keys::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let mut master_keys = BTreeMap::new();
|
||||
let mut self_signing_keys = BTreeMap::new();
|
||||
let mut user_signing_keys = BTreeMap::new();
|
||||
let mut device_keys = BTreeMap::new();
|
||||
let response = get_keys_helper(
|
||||
Some(sender_user),
|
||||
&body.device_keys,
|
||||
|u| u == sender_user,
|
||||
&db,
|
||||
)?;
|
||||
|
||||
for (user_id, device_ids) in &body.device_keys {
|
||||
if device_ids.is_empty() {
|
||||
let mut container = BTreeMap::new();
|
||||
for device_id in db.users.all_device_ids(user_id) {
|
||||
let device_id = device_id?;
|
||||
if let Some(mut keys) = db.users.get_device_keys(user_id, &device_id)? {
|
||||
let metadata = db
|
||||
.users
|
||||
.get_device_metadata(user_id, &device_id)?
|
||||
.ok_or_else(|| {
|
||||
Error::bad_database("all_device_keys contained nonexistent device.")
|
||||
})?;
|
||||
|
||||
keys.unsigned = UnsignedDeviceInfo {
|
||||
device_display_name: metadata.display_name,
|
||||
};
|
||||
|
||||
container.insert(device_id, keys);
|
||||
}
|
||||
}
|
||||
device_keys.insert(user_id.clone(), container);
|
||||
} else {
|
||||
for device_id in device_ids {
|
||||
let mut container = BTreeMap::new();
|
||||
if let Some(mut keys) = db.users.get_device_keys(&user_id.clone(), &device_id)? {
|
||||
let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or(
|
||||
Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Tried to get keys for nonexistent device.",
|
||||
),
|
||||
)?;
|
||||
|
||||
keys.unsigned = UnsignedDeviceInfo {
|
||||
device_display_name: metadata.display_name,
|
||||
};
|
||||
|
||||
container.insert(device_id.clone(), keys);
|
||||
}
|
||||
device_keys.insert(user_id.clone(), container);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(master_key) = db.users.get_master_key(user_id, sender_user)? {
|
||||
master_keys.insert(user_id.clone(), master_key);
|
||||
}
|
||||
if let Some(self_signing_key) = db.users.get_self_signing_key(user_id, sender_user)? {
|
||||
self_signing_keys.insert(user_id.clone(), self_signing_key);
|
||||
}
|
||||
if user_id == sender_user {
|
||||
if let Some(user_signing_key) = db.users.get_user_signing_key(sender_user)? {
|
||||
user_signing_keys.insert(user_id.clone(), user_signing_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(get_keys::Response {
|
||||
master_keys,
|
||||
self_signing_keys,
|
||||
user_signing_keys,
|
||||
device_keys,
|
||||
failures: BTreeMap::new(),
|
||||
}
|
||||
.into())
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
|
@ -356,3 +297,81 @@ pub async fn get_key_changes_route(
|
|||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
pub fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||
sender_user: Option<&UserId>,
|
||||
device_keys_input: &BTreeMap<UserId, Vec<Box<DeviceId>>>,
|
||||
allowed_signatures: F,
|
||||
db: &Database,
|
||||
) -> Result<get_keys::Response> {
|
||||
let mut master_keys = BTreeMap::new();
|
||||
let mut self_signing_keys = BTreeMap::new();
|
||||
let mut user_signing_keys = BTreeMap::new();
|
||||
let mut device_keys = BTreeMap::new();
|
||||
|
||||
for (user_id, device_ids) in device_keys_input {
|
||||
if device_ids.is_empty() {
|
||||
let mut container = BTreeMap::new();
|
||||
for device_id in db.users.all_device_ids(user_id) {
|
||||
let device_id = device_id?;
|
||||
if let Some(mut keys) = db.users.get_device_keys(user_id, &device_id)? {
|
||||
let metadata = db
|
||||
.users
|
||||
.get_device_metadata(user_id, &device_id)?
|
||||
.ok_or_else(|| {
|
||||
Error::bad_database("all_device_keys contained nonexistent device.")
|
||||
})?;
|
||||
|
||||
keys.unsigned = UnsignedDeviceInfo {
|
||||
device_display_name: metadata.display_name,
|
||||
};
|
||||
|
||||
container.insert(device_id, keys);
|
||||
}
|
||||
}
|
||||
device_keys.insert(user_id.clone(), container);
|
||||
} else {
|
||||
for device_id in device_ids {
|
||||
let mut container = BTreeMap::new();
|
||||
if let Some(mut keys) = db.users.get_device_keys(&user_id.clone(), &device_id)? {
|
||||
let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or(
|
||||
Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Tried to get keys for nonexistent device.",
|
||||
),
|
||||
)?;
|
||||
|
||||
keys.unsigned = UnsignedDeviceInfo {
|
||||
device_display_name: metadata.display_name,
|
||||
};
|
||||
|
||||
container.insert(device_id.clone(), keys);
|
||||
}
|
||||
device_keys.insert(user_id.clone(), container);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(master_key) = db.users.get_master_key(user_id, &allowed_signatures)? {
|
||||
master_keys.insert(user_id.clone(), master_key);
|
||||
}
|
||||
if let Some(self_signing_key) = db
|
||||
.users
|
||||
.get_self_signing_key(user_id, &allowed_signatures)?
|
||||
{
|
||||
self_signing_keys.insert(user_id.clone(), self_signing_key);
|
||||
}
|
||||
if Some(user_id) == sender_user {
|
||||
if let Some(user_signing_key) = db.users.get_user_signing_key(user_id)? {
|
||||
user_signing_keys.insert(user_id.clone(), user_signing_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(get_keys::Response {
|
||||
master_keys,
|
||||
self_signing_keys,
|
||||
user_signing_keys,
|
||||
device_keys,
|
||||
failures: BTreeMap::new(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
pdu::{PduBuilder, PduEvent},
|
||||
server_server, utils, ConduitResult, Database, Error, Result, Ruma,
|
||||
};
|
||||
use log::{error, warn};
|
||||
use log::{debug, error, warn};
|
||||
use member::{MemberEventContent, MembershipState};
|
||||
use rocket::futures;
|
||||
use ruma::{
|
||||
|
@ -29,9 +29,10 @@ use ruma::{
|
|||
uint, EventId, RoomId, RoomVersionId, ServerName, UserId,
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
collections::{btree_map::Entry, BTreeMap, HashSet},
|
||||
convert::{TryFrom, TryInto},
|
||||
sync::{Arc, RwLock},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
|
@ -703,6 +704,38 @@ async fn validate_and_add_event_id(
|
|||
error!("{:?}: {:?}", pdu, e);
|
||||
Error::BadServerResponse("Invalid PDU in server response")
|
||||
})?;
|
||||
let event_id = EventId::try_from(&*format!(
|
||||
"${}",
|
||||
ruma::signatures::reference_hash(&value, &room_version)
|
||||
.expect("ruma can calculate reference hashes")
|
||||
))
|
||||
.expect("ruma's reference hashes are valid event ids");
|
||||
|
||||
let back_off = |id| match db.globals.bad_event_ratelimiter.write().unwrap().entry(id) {
|
||||
Entry::Vacant(e) => {
|
||||
e.insert((Instant::now(), 1));
|
||||
}
|
||||
Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
|
||||
};
|
||||
|
||||
if let Some((time, tries)) = db
|
||||
.globals
|
||||
.bad_event_ratelimiter
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&event_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries);
|
||||
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
||||
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
||||
}
|
||||
|
||||
if time.elapsed() < min_elapsed_duration {
|
||||
debug!("Backing off from {}", event_id);
|
||||
return Err(Error::BadServerResponse("bad event, still backing off"));
|
||||
}
|
||||
}
|
||||
|
||||
server_server::fetch_required_signing_keys(&value, pub_key_map, db).await?;
|
||||
if let Err(e) = ruma::signatures::verify_event(
|
||||
|
@ -712,17 +745,11 @@ async fn validate_and_add_event_id(
|
|||
&value,
|
||||
room_version,
|
||||
) {
|
||||
warn!("Event failed verification: {}", e);
|
||||
warn!("Event {} failed verification: {}", event_id, e);
|
||||
back_off(event_id);
|
||||
return Err(Error::BadServerResponse("Event failed verification."));
|
||||
}
|
||||
|
||||
let event_id = EventId::try_from(&*format!(
|
||||
"${}",
|
||||
ruma::signatures::reference_hash(&value, &room_version)
|
||||
.expect("ruma can calculate reference hashes")
|
||||
))
|
||||
.expect("ruma's reference hashes are valid event ids");
|
||||
|
||||
value.insert(
|
||||
"event_id".to_owned(),
|
||||
CanonicalJsonValue::String(event_id.as_str().to_owned()),
|
||||
|
|
|
@ -5,12 +5,14 @@ use ruma::{
|
|||
error::ErrorKind,
|
||||
r0::{read_marker::set_read_marker, receipt::create_receipt},
|
||||
},
|
||||
events::{AnyEphemeralRoomEvent, AnyEvent, EventType},
|
||||
events::{AnyEphemeralRoomEvent, EventType},
|
||||
receipt::ReceiptType,
|
||||
MilliSecondsSinceUnixEpoch,
|
||||
};
|
||||
|
||||
#[cfg(feature = "conduit_bin")]
|
||||
use rocket::post;
|
||||
use std::{collections::BTreeMap, time::SystemTime};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "conduit_bin",
|
||||
|
@ -27,7 +29,6 @@ pub async fn set_read_marker_route(
|
|||
content: ruma::events::fully_read::FullyReadEventContent {
|
||||
event_id: body.fully_read.clone(),
|
||||
},
|
||||
room_id: body.room_id.clone(),
|
||||
};
|
||||
db.account_data.update(
|
||||
Some(&body.room_id),
|
||||
|
@ -54,26 +55,23 @@ pub async fn set_read_marker_route(
|
|||
user_receipts.insert(
|
||||
sender_user.clone(),
|
||||
ruma::events::receipt::Receipt {
|
||||
ts: Some(SystemTime::now()),
|
||||
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
||||
},
|
||||
);
|
||||
|
||||
let mut receipts = BTreeMap::new();
|
||||
receipts.insert(ReceiptType::Read, user_receipts);
|
||||
|
||||
let mut receipt_content = BTreeMap::new();
|
||||
receipt_content.insert(
|
||||
event.to_owned(),
|
||||
ruma::events::receipt::Receipts {
|
||||
read: Some(user_receipts),
|
||||
},
|
||||
);
|
||||
receipt_content.insert(event.to_owned(), receipts);
|
||||
|
||||
db.rooms.edus.readreceipt_update(
|
||||
&sender_user,
|
||||
&body.room_id,
|
||||
AnyEvent::Ephemeral(AnyEphemeralRoomEvent::Receipt(
|
||||
ruma::events::receipt::ReceiptEvent {
|
||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||
room_id: body.room_id.clone(),
|
||||
},
|
||||
)),
|
||||
AnyEphemeralRoomEvent::Receipt(ruma::events::receipt::ReceiptEvent {
|
||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||
room_id: body.room_id.clone(),
|
||||
}),
|
||||
&db.globals,
|
||||
)?;
|
||||
}
|
||||
|
@ -112,26 +110,22 @@ pub async fn create_receipt_route(
|
|||
user_receipts.insert(
|
||||
sender_user.clone(),
|
||||
ruma::events::receipt::Receipt {
|
||||
ts: Some(SystemTime::now()),
|
||||
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
||||
},
|
||||
);
|
||||
let mut receipts = BTreeMap::new();
|
||||
receipts.insert(ReceiptType::Read, user_receipts);
|
||||
|
||||
let mut receipt_content = BTreeMap::new();
|
||||
receipt_content.insert(
|
||||
body.event_id.to_owned(),
|
||||
ruma::events::receipt::Receipts {
|
||||
read: Some(user_receipts),
|
||||
},
|
||||
);
|
||||
receipt_content.insert(body.event_id.to_owned(), receipts);
|
||||
|
||||
db.rooms.edus.readreceipt_update(
|
||||
&sender_user,
|
||||
&body.room_id,
|
||||
AnyEvent::Ephemeral(AnyEphemeralRoomEvent::Receipt(
|
||||
ruma::events::receipt::ReceiptEvent {
|
||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||
room_id: body.room_id.clone(),
|
||||
},
|
||||
)),
|
||||
AnyEphemeralRoomEvent::Receipt(ruma::events::receipt::ReceiptEvent {
|
||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||
room_id: body.room_id.clone(),
|
||||
}),
|
||||
&db.globals,
|
||||
)?;
|
||||
|
||||
|
|
|
@ -422,7 +422,7 @@ pub async fn sync_events_route(
|
|||
}
|
||||
|
||||
let joined_room = sync_events::JoinedRoom {
|
||||
account_data: sync_events::AccountData {
|
||||
account_data: sync_events::RoomAccountData {
|
||||
events: db
|
||||
.account_data
|
||||
.changes_since(Some(&room_id), &sender_user, since)?
|
||||
|
@ -506,7 +506,7 @@ pub async fn sync_events_route(
|
|||
left_rooms.insert(
|
||||
room_id.clone(),
|
||||
sync_events::LeftRoom {
|
||||
account_data: sync_events::AccountData { events: Vec::new() },
|
||||
account_data: sync_events::RoomAccountData { events: Vec::new() },
|
||||
timeline: sync_events::Timeline {
|
||||
limited: false,
|
||||
prev_batch: Some(next_batch.clone()),
|
||||
|
@ -577,7 +577,7 @@ pub async fn sync_events_route(
|
|||
.map(|(_, v)| Raw::from(v))
|
||||
.collect(),
|
||||
},
|
||||
account_data: sync_events::AccountData {
|
||||
account_data: sync_events::GlobalAccountData {
|
||||
events: db
|
||||
.account_data
|
||||
.changes_since(None, &sender_user, since)?
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue