From 56d4742201dec585483ff73177163dcb83ee2412 Mon Sep 17 00:00:00 2001 From: timokoesters Date: Thu, 11 Jun 2020 10:03:08 +0200 Subject: [PATCH] improvement: log bad database errors automatically --- src/client_server.rs | 124 +++++++++++++------------ src/database/account_data.rs | 14 ++- src/database/global_edus.rs | 2 +- src/database/globals.rs | 6 +- src/database/media.rs | 36 ++++---- src/database/rooms.rs | 170 +++++++++++++++++++++-------------- src/database/rooms/edus.rs | 32 ++++--- src/database/uiaa.rs | 2 +- src/database/users.rs | 81 ++++++++++------- src/error.rs | 9 ++ src/pdu.rs | 2 +- src/ruma_wrapper.rs | 5 +- sytest/sytest-whitelist | 3 +- 13 files changed, 278 insertions(+), 208 deletions(-) diff --git a/src/client_server.rs b/src/client_server.rs index 06748af6..3a2e771b 100644 --- a/src/client_server.rs +++ b/src/client_server.rs @@ -362,17 +362,14 @@ pub fn get_pushrules_all_route( "PushRules event not found.", ))? .deserialize() - .map_err(|_| Error::BadRequest( - ErrorKind::NotFound, - "PushRules event in db is invalid.", - ))? + .map_err(|_| Error::BadRequest(ErrorKind::NotFound, "PushRules event in db is invalid."))? { Ok(get_pushrules_all::Response { global: pushrules.content.global, } .into()) } else { - Err(Error::BadDatabase("Pushrules event has wrong content.")) + Err(Error::bad_database("Pushrules event has wrong content.")) } } @@ -507,15 +504,17 @@ pub fn set_displayname_route( db.rooms .room_state(&room_id)? .get(&(EventType::RoomMember, user_id.to_string())) - .ok_or(Error::BadDatabase( - "Tried to send displayname update for user not in the room.", - ))? + .ok_or_else(|| { + Error::bad_database( + "Tried to send displayname update for user not in the room.", + ) + })? .content .clone(), ) - .map_err(|_| Error::BadDatabase("Database contains invalid PDU."))? + .map_err(|_| Error::bad_database("Database contains invalid PDU."))? .deserialize() - .map_err(|_| Error::BadDatabase("Database contains invalid PDU."))? + .map_err(|_| Error::bad_database("Database contains invalid PDU."))? }) .expect("event is valid, we just created it"), None, @@ -596,15 +595,17 @@ pub fn set_avatar_url_route( db.rooms .room_state(&room_id)? .get(&(EventType::RoomMember, user_id.to_string())) - .ok_or(Error::BadDatabase( - "Tried to send avatar url update for user not in the room.", - ))? + .ok_or_else(|| { + Error::bad_database( + "Tried to send avatar url update for user not in the room.", + ) + })? .content .clone(), ) - .map_err(|_| Error::BadDatabase("Database contains invalid PDU."))? + .map_err(|_| Error::bad_database("Database contains invalid PDU."))? .deserialize() - .map_err(|_| Error::BadDatabase("Database contains invalid PDU."))? + .map_err(|_| Error::bad_database("Database contains invalid PDU."))? }) .expect("event is valid, we just created it"), None, @@ -744,9 +745,12 @@ pub fn get_keys_route( for result in db.users.all_device_keys(&user_id.clone()) { let (device_id, mut keys) = result?; - let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or( - Error::BadDatabase("all_device_keys contained nonexistent device."), - )?; + 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 = Some(keys::UnsignedDeviceInfo { device_display_name: metadata.display_name, @@ -912,7 +916,7 @@ pub fn create_room_route( let user_id = body.user_id.as_ref().expect("user is authenticated"); let room_id = RoomId::new(db.globals.server_name()) - .map_err(|_| Error::BadDatabase("Server name is invalid."))?; + .map_err(|_| Error::bad_database("Server name is invalid."))?; let alias = body .room_alias_name @@ -1281,9 +1285,9 @@ pub fn join_room_by_id_route( let mut event = serde_json::from_value::>( pdu.content.clone(), ) - .map_err(|_| Error::BadDatabase("Invalid member event in db."))? + .map_err(|_| Error::bad_database("Invalid member event in db."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid member event in db."))?; + .map_err(|_| Error::bad_database("Invalid member event in db."))?; event.membership = member::MembershipState::Join; event.displayname = db.users.displayname(&user_id)?; event.avatar_url = db.users.avatar_url(&user_id)?; @@ -1356,9 +1360,9 @@ pub fn leave_room_route( .content .clone(), ) - .map_err(|_| Error::BadDatabase("Invalid member event in database."))? + .map_err(|_| Error::bad_database("Invalid member event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid member event in database."))?; + .map_err(|_| Error::bad_database("Invalid member event in database."))?; event.membership = member::MembershipState::Leave; @@ -1396,9 +1400,9 @@ pub fn kick_user_route( .content .clone(), ) - .map_err(|_| Error::BadDatabase("Invalid member event in database."))? + .map_err(|_| Error::bad_database("Invalid member event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid member event in database."))?; + .map_err(|_| Error::bad_database("Invalid member event in database."))?; event.membership = ruma::events::room::member::MembershipState::Leave; // TODO: reason @@ -1442,9 +1446,9 @@ pub fn ban_user_route( let mut event = serde_json::from_value::>( event.content.clone(), ) - .map_err(|_| Error::BadDatabase("Invalid member event in database."))? + .map_err(|_| Error::bad_database("Invalid member event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid member event in database."))?; + .map_err(|_| Error::bad_database("Invalid member event in database."))?; event.membership = ruma::events::room::member::MembershipState::Ban; Ok(event) }, @@ -1484,9 +1488,9 @@ pub fn unban_user_route( .content .clone(), ) - .map_err(|_| Error::BadDatabase("Invalid member event in database."))? + .map_err(|_| Error::bad_database("Invalid member event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid member event in database."))?; + .map_err(|_| Error::bad_database("Invalid member event in database."))?; event.membership = ruma::events::room::member::MembershipState::Leave; @@ -1646,18 +1650,18 @@ pub async fn get_public_rooms_filtered_route( Ok(serde_json::from_value::< EventJson, >(s.content.clone()) - .map_err(|_| Error::BadDatabase("Invalid canonical alias event in database."))? + .map_err(|_| Error::bad_database("Invalid canonical alias event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid canonical alias event in database."))? + .map_err(|_| Error::bad_database("Invalid canonical alias event in database."))? .alias) })?, name: state.get(&(EventType::RoomName, "".to_owned())).map_or(Ok::<_, Error>(None), |s| { Ok(serde_json::from_value::>( s.content.clone(), ) - .map_err(|_| Error::BadDatabase("Invalid room name event in database."))? + .map_err(|_| Error::bad_database("Invalid room name event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid room name event in database."))? + .map_err(|_| Error::bad_database("Invalid room name event in database."))? .name() .map(|n| n.to_owned())) })?, @@ -1667,36 +1671,36 @@ pub async fn get_public_rooms_filtered_route( Ok(Some(serde_json::from_value::< EventJson, >(s.content.clone()) - .map_err(|_| Error::BadDatabase("Invalid room topic event in database."))? + .map_err(|_| Error::bad_database("Invalid room topic event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid room topic event in database."))? + .map_err(|_| Error::bad_database("Invalid room topic event in database."))? .topic)) })?, world_readable: state.get(&(EventType::RoomHistoryVisibility, "".to_owned())).map_or(Ok::<_, Error>(false), |s| { Ok(serde_json::from_value::< EventJson, >(s.content.clone()) - .map_err(|_| Error::BadDatabase("Invalid room history visibility event in database."))? + .map_err(|_| Error::bad_database("Invalid room history visibility event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid room history visibility event in database."))? + .map_err(|_| Error::bad_database("Invalid room history visibility event in database."))? .history_visibility == history_visibility::HistoryVisibility::WorldReadable) })?, guest_can_join: state.get(&(EventType::RoomGuestAccess, "".to_owned())).map_or(Ok::<_, Error>(false), |s| { Ok(serde_json::from_value::< EventJson, >(s.content.clone()) - .map_err(|_| Error::BadDatabase("Invalid room guest access event in database."))? + .map_err(|_| Error::bad_database("Invalid room guest access event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid room guest access event in database."))? + .map_err(|_| Error::bad_database("Invalid room guest access event in database."))? .guest_access == guest_access::GuestAccess::CanJoin) })?, avatar_url: state.get(&(EventType::RoomAvatar, "".to_owned())).map_or( Ok::<_, Error>(None),|s| { Ok(Some(serde_json::from_value::< EventJson, >(s.content.clone()) - .map_err(|_| Error::BadDatabase("Invalid room avatar event in database."))? + .map_err(|_| Error::bad_database("Invalid room avatar event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid room avatar event in database."))? + .map_err(|_| Error::bad_database("Invalid room avatar event in database."))? .url)) })?, }; @@ -1986,7 +1990,7 @@ pub fn get_state_events_for_key_route( Ok(get_state_events_for_key::Response { content: serde_json::value::to_raw_value(&event.content) - .map_err(|_| Error::BadDatabase("Invalid event content in database"))?, + .map_err(|_| Error::bad_database("Invalid event content in database"))?, } .into()) } @@ -2021,7 +2025,7 @@ pub fn get_state_events_for_empty_key_route( Ok(get_state_events_for_empty_key::Response { content: serde_json::value::to_raw_value(event) - .map_err(|_| Error::BadDatabase("Invalid event content in database"))?, + .map_err(|_| Error::bad_database("Invalid event content in database"))?, } .into()) } @@ -2064,9 +2068,9 @@ pub fn sync_route( let content = serde_json::from_value::< EventJson, >(pdu.content.clone()) - .map_err(|_| Error::BadDatabase("Invalid PDU in database."))? + .map_err(|_| Error::bad_database("Invalid PDU in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid PDU in database."))?; + .map_err(|_| Error::bad_database("Invalid PDU in database."))?; if content.membership == ruma::events::room::member::MembershipState::Join { joined_since_last_sync = true; // Both send_member_count and joined_since_last_sync are set. There's nothing more @@ -2099,9 +2103,9 @@ pub fn sync_route( let content = serde_json::from_value::< EventJson, >(pdu.content.clone()) - .map_err(|_| Error::BadDatabase("Invalid member event in database."))? + .map_err(|_| Error::bad_database("Invalid member event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid member event in database."))?; + .map_err(|_| Error::bad_database("Invalid member event in database."))?; if let Some(state_key) = &pdu.state_key { let current_content = serde_json::from_value::< @@ -2109,15 +2113,19 @@ pub fn sync_route( >( state .get(&(EventType::RoomMember, state_key.clone())) - .ok_or(Error::BadDatabase( - "A user that joined once has no member event anymore.", - ))? + .ok_or_else(|| { + Error::bad_database( + "A user that joined once has no member event anymore.", + ) + })? .content .clone(), ) - .map_err(|_| Error::BadDatabase("Invalid member event in database."))? + .map_err(|_| Error::bad_database("Invalid member event in database."))? .deserialize() - .map_err(|_| Error::BadDatabase("Invalid member event in database."))?; + .map_err(|_| { + Error::bad_database("Invalid member event in database.") + })?; // The membership was and still is invite or join if matches!( @@ -2192,7 +2200,7 @@ pub fn sync_route( Ok(Some( db.rooms .get_pdu_count(&e.event_id)? - .ok_or(Error::BadDatabase("Can't find count from event in db."))? + .ok_or_else(|| Error::bad_database("Can't find count from event in db."))? .to_string(), )) })?; @@ -2355,7 +2363,7 @@ pub fn sync_route( .map(|edu| { let mut edu = edu? .deserialize() - .map_err(|_| Error::BadDatabase("EDU in database is invalid."))?; + .map_err(|_| Error::bad_database("EDU in database is invalid."))?; if let Some(timestamp) = edu.content.last_active_ago { let last_active_ago = js_int::UInt::try_from(utils::millis_since_unix_epoch()) @@ -2444,7 +2452,7 @@ pub fn get_context_route( Ok::<_, Error>(Some( db.rooms .get_pdu_count(&e.event_id)? - .ok_or(Error::BadDatabase("Can't find count from event in db."))? + .ok_or_else(|| Error::bad_database("Can't find count from event in db."))? .to_string(), )) })?; @@ -2470,7 +2478,7 @@ pub fn get_context_route( Ok::<_, Error>(Some( db.rooms .get_pdu_count(&e.event_id)? - .ok_or(Error::BadDatabase("Can't find count from event in db."))? + .ok_or_else(|| Error::bad_database("Can't find count from event in db."))? .to_string(), )) })?; @@ -2534,7 +2542,7 @@ pub fn get_message_events_route( Ok(Some( db.rooms .get_pdu_count(&e.event_id)? - .ok_or(Error::BadDatabase("Can't find count from event in db."))? + .ok_or_else(|| Error::bad_database("Can't find count from event in db."))? .to_string(), )) })?; @@ -2569,7 +2577,7 @@ pub fn get_message_events_route( Ok(Some( db.rooms .get_pdu_count(&e.event_id)? - .ok_or(Error::BadDatabase("Can't find count from event in db."))? + .ok_or_else(|| Error::bad_database("Can't find count from event in db."))? .to_string(), )) })?; diff --git a/src/database/account_data.rs b/src/database/account_data.rs index f7c564d3..befd9374 100644 --- a/src/database/account_data.rs +++ b/src/database/account_data.rs @@ -115,16 +115,14 @@ impl AccountData { .map(|(k, v)| { Ok::<_, Error>(( EventType::try_from( - utils::string_from_bytes( - k.rsplit(|&b| b == 0xff) - .next() - .ok_or(Error::BadDatabase("RoomUserData ID in db is invalid."))?, - ) - .map_err(|_| Error::BadDatabase("RoomUserData ID in db is invalid."))?, + utils::string_from_bytes(k.rsplit(|&b| b == 0xff).next().ok_or_else( + || Error::bad_database("RoomUserData ID in db is invalid."), + )?) + .map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?, ) - .map_err(|_| Error::BadDatabase("RoomUserData ID in db is invalid."))?, + .map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?, serde_json::from_slice::>(&v).map_err(|_| { - Error::BadDatabase("Database contains invalid account data.") + Error::bad_database("Database contains invalid account data.") })?, )) }) diff --git a/src/database/global_edus.rs b/src/database/global_edus.rs index 7d1ac20c..f58c7d60 100644 --- a/src/database/global_edus.rs +++ b/src/database/global_edus.rs @@ -57,7 +57,7 @@ impl GlobalEdus { .filter_map(|r| r.ok()) .map(|(_, v)| { Ok(serde_json::from_slice(&v) - .map_err(|_| Error::BadDatabase("Invalid presence event in db."))?) + .map_err(|_| Error::bad_database("Invalid presence event in db."))?) })) } } diff --git a/src/database/globals.rs b/src/database/globals.rs index 32cddd8b..a767d8ad 100644 --- a/src/database/globals.rs +++ b/src/database/globals.rs @@ -18,7 +18,7 @@ impl Globals { .expect("utils::generate_keypair always returns Some"), "key1".to_owned(), ) - .map_err(|_| Error::BadDatabase("Private or public keys are invalid."))?; + .map_err(|_| Error::bad_database("Private or public keys are invalid."))?; Ok(Self { globals, @@ -49,13 +49,13 @@ impl Globals { .update_and_fetch(COUNTER, utils::increment)? .expect("utils::increment will always put in a value"), ) - .map_err(|_| Error::BadDatabase("Count has invalid bytes."))?) + .map_err(|_| Error::bad_database("Count has invalid bytes."))?) } pub fn current_count(&self) -> Result { self.globals.get(COUNTER)?.map_or(Ok(0_u64), |bytes| { Ok(utils::u64_from_bytes(&bytes) - .map_err(|_| Error::BadDatabase("Count has invalid bytes."))?) + .map_err(|_| Error::bad_database("Count has invalid bytes."))?) }) } diff --git a/src/database/media.rs b/src/database/media.rs index f70e9249..0d0820d3 100644 --- a/src/database/media.rs +++ b/src/database/media.rs @@ -43,21 +43,20 @@ impl Media { let content_type = utils::string_from_bytes( parts .next() - .ok_or(Error::BadDatabase("Invalid Media ID in db"))?, + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?, ) - .map_err(|_| Error::BadDatabase("Invalid content type in db."))?; + .map_err(|_| Error::bad_database("Content type in mediaid_file is invalid unicode."))?; let filename_bytes = parts .next() - .ok_or(Error::BadDatabase("Media ID in db is invalid."))?; + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; let filename = if filename_bytes.is_empty() { None } else { - Some( - utils::string_from_bytes(filename_bytes) - .map_err(|_| Error::BadDatabase("Filename in db is invalid."))?, - ) + Some(utils::string_from_bytes(filename_bytes).map_err(|_| { + Error::bad_database("Filename in mediaid_file is invalid unicode.") + })?) }; Ok(Some((filename, content_type, file.to_vec()))) @@ -94,20 +93,20 @@ impl Media { let content_type = utils::string_from_bytes( parts .next() - .ok_or(Error::BadDatabase("Invalid Media ID in db"))?, + .ok_or_else(|| Error::bad_database("Invalid Media ID in db"))?, ) - .map_err(|_| Error::BadDatabase("Invalid content type in db."))?; + .map_err(|_| Error::bad_database("Content type in mediaid_file is invalid unicode."))?; let filename_bytes = parts .next() - .ok_or(Error::BadDatabase("Media ID in db is invalid."))?; + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; let filename = if filename_bytes.is_empty() { None } else { Some( utils::string_from_bytes(filename_bytes) - .map_err(|_| Error::BadDatabase("Filename in db is invalid."))?, + .map_err(|_| Error::bad_database("Filename in db is invalid."))?, ) }; @@ -120,21 +119,20 @@ impl Media { let content_type = utils::string_from_bytes( parts .next() - .ok_or(Error::BadDatabase("Media ID in db is invalid"))?, + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?, ) - .map_err(|_| Error::BadDatabase("Invalid content type in db."))?; + .map_err(|_| Error::bad_database("Content type in mediaid_file is invalid unicode."))?; let filename_bytes = parts .next() - .ok_or(Error::BadDatabase("Media ID in db is invalid"))?; + .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; let filename = if filename_bytes.is_empty() { None } else { - Some( - utils::string_from_bytes(filename_bytes) - .map_err(|_| Error::BadDatabase("Filename in db is invalid."))?, - ) + Some(utils::string_from_bytes(filename_bytes).map_err(|_| { + Error::bad_database("Filename in mediaid_file is invalid unicode.") + })?) }; if let Ok(image) = image::load_from_memory(&file) { @@ -147,7 +145,7 @@ impl Media { let width_index = thumbnail_key .iter() .position(|&b| b == 0xff) - .ok_or(Error::BadDatabase("mediaid is invalid"))? + .ok_or_else(|| Error::bad_database("Media in db is invalid."))? + 1; let mut widthheight = width.to_be_bytes().to_vec(); widthheight.extend_from_slice(&height.to_be_bytes()); diff --git a/src/database/rooms.rs b/src/database/rooms.rs index 799a7cb5..6b3b9c58 100644 --- a/src/database/rooms.rs +++ b/src/database/rooms.rs @@ -65,14 +65,14 @@ impl Rooms { .map(|value| { Ok::<_, Error>( serde_json::from_slice::(&value?) - .map_err(|_| Error::BadDatabase("Invalid PDU in db."))?, + .map_err(|_| Error::bad_database("Invalid PDU in db."))?, ) }) { let pdu = pdu?; - let state_key = pdu.state_key.clone().ok_or(Error::BadDatabase( - "Room state contains event without state_key.", - ))?; + let state_key = pdu.state_key.clone().ok_or_else(|| { + Error::bad_database("Room state contains event without state_key.") + })?; hashmap.insert((pdu.kind.clone(), state_key), pdu); } Ok(hashmap) @@ -87,7 +87,7 @@ impl Rooms { utils::u64_from_bytes( &pdu_id[pdu_id.len() - mem::size_of::()..pdu_id.len()], ) - .map_err(|_| Error::BadDatabase("PDU has invalid count bytes."))?, + .map_err(|_| Error::bad_database("PDU has invalid count bytes."))?, )) }) } @@ -98,10 +98,10 @@ impl Rooms { .get(event_id.to_string().as_bytes())? .map_or(Ok(None), |pdu_id| { Ok(Some( - serde_json::from_slice(&self.pduid_pdu.get(pdu_id)?.ok_or( - Error::BadDatabase("eventid_pduid points to nonexistent pdu."), - )?) - .map_err(|_| Error::BadDatabase("Invalid PDU in db."))?, + serde_json::from_slice(&self.pduid_pdu.get(pdu_id)?.ok_or_else(|| { + Error::bad_database("eventid_pduid points to nonexistent pdu.") + })?) + .map_err(|_| Error::bad_database("Invalid PDU in db."))?, )) }) } @@ -119,10 +119,10 @@ impl Rooms { .get(event_id.to_string().as_bytes())? .map_or(Ok(None), |pdu_id| { Ok(Some( - serde_json::from_slice(&self.pduid_pdu.get(pdu_id)?.ok_or( - Error::BadDatabase("eventid_pduid points to nonexistent pdu."), - )?) - .map_err(|_| Error::BadDatabase("Invalid PDU in db."))?, + serde_json::from_slice(&self.pduid_pdu.get(pdu_id)?.ok_or_else(|| { + Error::bad_database("eventid_pduid points to nonexistent pdu.") + })?) + .map_err(|_| Error::bad_database("Invalid PDU in db."))?, )) }) } @@ -131,7 +131,7 @@ impl Rooms { self.pduid_pdu.get(pdu_id)?.map_or(Ok(None), |pdu| { Ok(Some( serde_json::from_slice(&pdu) - .map_err(|_| Error::BadDatabase("Invalid PDU in db."))?, + .map_err(|_| Error::bad_database("Invalid PDU in db."))?, )) }) } @@ -165,8 +165,10 @@ impl Rooms { .values() .map(|bytes| { Ok::<_, Error>( - serde_json::from_slice(&bytes?) - .map_err(|_| Error::BadDatabase("Invalid EventID in roomid_pduleaves."))?, + EventId::try_from(utils::string_from_bytes(&bytes?).map_err(|_| { + Error::bad_database("EventID in roomid_pduleaves is invalid unicode.") + })?) + .map_err(|_| Error::bad_database("EventId in roomid_pduleaves is invalid."))?, ) }) { @@ -237,7 +239,7 @@ impl Rooms { ) .expect("EventJson::from_value always works.") .deserialize() - .map_err(|_| Error::BadDatabase("Invalid PowerLevels event in db."))?, + .map_err(|_| Error::bad_database("Invalid PowerLevels event in db."))?, ) }, )?; @@ -251,7 +253,7 @@ impl Rooms { ) .expect("EventJson::from_value always works.") .deserialize() - .map_err(|_| Error::BadDatabase("Invalid Member event in db."))? + .map_err(|_| Error::bad_database("Invalid Member event in db."))? .membership, ) })?; @@ -287,7 +289,7 @@ impl Rooms { ) .expect("EventJson::from_value always works.") .deserialize() - .map_err(|_| Error::BadDatabase("Invalid Member event in db."))? + .map_err(|_| Error::bad_database("Invalid Member event in db."))? .membership, ) })?; @@ -297,7 +299,7 @@ impl Rooms { >(content.clone()) .expect("EventJson::from_value always works.") .deserialize() - .map_err(|_| Error::BadDatabase("Invalid Member event in db."))? + .map_err(|_| Error::bad_database("Invalid Member event in db."))? .membership; let target_power = power_levels.users.get(&target_user_id).map_or_else( @@ -322,7 +324,7 @@ impl Rooms { .expect("EventJson::from_value always works.") .deserialize() .map_err(|_| { - Error::BadDatabase("Database contains invalid JoinRules event") + Error::bad_database("Database contains invalid JoinRules event") })? .join_rule) })?; @@ -334,7 +336,9 @@ impl Rooms { ErrorKind::Unknown, "Membership can't be the first event", ))?)? - .ok_or(Error::BadDatabase("PDU leaf points to invalid event!"))?; + .ok_or_else(|| { + Error::bad_database("PDU leaf points to invalid event!") + })?; if prev_event.kind == EventType::RoomCreate && prev_event.prev_events.is_empty() { @@ -466,7 +470,7 @@ impl Rooms { prev_events, depth: depth .try_into() - .map_err(|_| Error::BadDatabase("Depth is invalid"))?, + .map_err(|_| Error::bad_database("Depth is invalid"))?, auth_events: Vec::new(), redacts: redacts.clone(), unsigned, @@ -588,7 +592,7 @@ impl Rooms { .take_while(move |(k, _)| k.starts_with(&prefix)) .map(|(_, v)| { Ok(serde_json::from_slice(&v) - .map_err(|_| Error::BadDatabase("PDU in db is invalid."))?) + .map_err(|_| Error::bad_database("PDU in db is invalid."))?) })) } @@ -615,7 +619,7 @@ impl Rooms { .take_while(move |(k, _)| k.starts_with(&prefix)) .map(|(_, v)| { Ok(serde_json::from_slice(&v) - .map_err(|_| Error::BadDatabase("PDU in db is invalid."))?) + .map_err(|_| Error::bad_database("PDU in db is invalid."))?) }) } @@ -641,7 +645,7 @@ impl Rooms { .take_while(move |(k, _)| k.starts_with(&prefix)) .map(|(_, v)| { Ok(serde_json::from_slice(&v) - .map_err(|_| Error::BadDatabase("PDU in db is invalid."))?) + .map_err(|_| Error::bad_database("PDU in db is invalid."))?) }) } @@ -650,7 +654,7 @@ impl Rooms { 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("PDU ID points to invalid PDU."))?; + .ok_or_else(|| Error::bad_database("PDU ID points to invalid PDU."))?; pdu.redact()?; self.replace_pdu(&pdu_id, &pdu)?; Ok(()) @@ -751,9 +755,12 @@ impl Rooms { self.alias_roomid .get(alias.alias())? .map_or(Ok(None), |bytes| { - Ok(Some(serde_json::from_slice(&bytes).map_err(|_| { - Error::BadDatabase("Room ID in alias_roomid is invalid.") - })?)) + Ok(Some( + RoomId::try_from(utils::string_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Room ID in alias_roomid is invalid unicode.") + })?) + .map_err(|_| Error::bad_database("Room ID in alias_roomid is invalid."))?, + )) }) } @@ -766,7 +773,7 @@ impl Rooms { .values() .map(|bytes| { Ok(serde_json::from_slice(&bytes?) - .map_err(|_| Error::BadDatabase("Alias in aliasid_alias is invalid."))?) + .map_err(|_| Error::bad_database("Alias in aliasid_alias is invalid."))?) }) } @@ -786,56 +793,75 @@ impl Rooms { pub fn public_rooms(&self) -> impl Iterator> { self.publicroomids.iter().keys().map(|bytes| { - Ok(serde_json::from_slice(&bytes?) - .map_err(|_| Error::BadDatabase("Room ID in publicroomids is invalid."))?) + Ok( + RoomId::try_from(utils::string_from_bytes(&bytes?).map_err(|_| { + Error::bad_database("Room ID in publicroomids is invalid unicode.") + })?) + .map_err(|_| Error::bad_database("Room ID in publicroomids is invalid."))?, + ) }) } - /// Returns an iterator over all rooms a user joined. + /// Returns an iterator over all joined members of a room. pub fn room_members(&self, room_id: &RoomId) -> impl Iterator> { self.roomuserid_joined .scan_prefix(room_id.to_string()) - .values() + .keys() .map(|key| { - Ok(serde_json::from_slice( - &key? - .rsplit(|&b| b == 0xff) - .next() - .ok_or(Error::BadDatabase("RoomUser ID is invalid."))?, + Ok(UserId::try_from( + utils::string_from_bytes( + &key? + .rsplit(|&b| b == 0xff) + .next() + .expect("rsplit always returns an element"), + ) + .map_err(|_| { + Error::bad_database("User ID in roomuserid_joined is invalid unicode.") + })?, ) - .map_err(|_| Error::BadDatabase("Invalid User ID in db."))?) + .map_err(|_| Error::bad_database("User ID in roomuserid_joined is invalid."))?) }) } - /// Returns an iterator over all rooms a user joined. + /// Returns an iterator over all invited members of a room. pub fn room_members_invited(&self, room_id: &RoomId) -> impl Iterator> { self.roomuserid_invited .scan_prefix(room_id.to_string()) .keys() .map(|key| { - Ok(serde_json::from_slice( - &key? - .rsplit(|&b| b == 0xff) - .next() - .ok_or(Error::BadDatabase("RoomUser ID is invalid."))?, + Ok(UserId::try_from( + utils::string_from_bytes( + &key? + .rsplit(|&b| b == 0xff) + .next() + .expect("rsplit always returns an element"), + ) + .map_err(|_| { + Error::bad_database("User ID in roomuserid_invited is invalid unicode.") + })?, ) - .map_err(|_| Error::BadDatabase("Invalid User ID in db."))?) + .map_err(|_| Error::bad_database("User ID in roomuserid_invited is invalid."))?) }) } - /// Returns an iterator over all rooms a user joined. + /// Returns an iterator over all left members of a room. pub fn rooms_joined(&self, user_id: &UserId) -> impl Iterator> { self.userroomid_joined .scan_prefix(user_id.to_string()) .keys() .map(|key| { - Ok(serde_json::from_slice( - &key? - .rsplit(|&b| b == 0xff) - .next() - .ok_or(Error::BadDatabase("UserRoom ID is invalid."))?, + Ok(RoomId::try_from( + utils::string_from_bytes( + &key? + .rsplit(|&b| b == 0xff) + .next() + .expect("rsplit always returns an element"), + ) + .map_err(|_| { + Error::bad_database("Room ID in userroomid_joined is invalid unicode.") + })?, ) - .map_err(|_| Error::BadDatabase("Invalid Room ID in db."))?) + .map_err(|_| Error::bad_database("Room ID in userroomid_joined is invalid."))?) }) } @@ -845,13 +871,18 @@ impl Rooms { .scan_prefix(&user_id.to_string()) .keys() .map(|key| { - Ok(serde_json::from_slice( - &key? - .rsplit(|&b| b == 0xff) - .next() - .ok_or(Error::BadDatabase("UserRoom ID is invalid."))?, + Ok(RoomId::try_from( + utils::string_from_bytes( + &key? + .rsplit(|&b| b == 0xff) + .next() + .expect("rsplit always returns an element"), + ) + .map_err(|_| { + Error::bad_database("Room ID in userroomid_invited is invalid unicode.") + })?, ) - .map_err(|_| Error::BadDatabase("Invalid Room ID in db."))?) + .map_err(|_| Error::bad_database("Room ID in userroomid_invited is invalid."))?) }) } @@ -861,13 +892,18 @@ impl Rooms { .scan_prefix(&user_id.to_string()) .keys() .map(|key| { - Ok(serde_json::from_slice( - &key? - .rsplit(|&b| b == 0xff) - .next() - .ok_or(Error::BadDatabase("UserRoom ID is invalid."))?, + Ok(RoomId::try_from( + utils::string_from_bytes( + &key? + .rsplit(|&b| b == 0xff) + .next() + .expect("rsplit always returns an element"), + ) + .map_err(|_| { + Error::bad_database("Room ID in userroomid_left is invalid unicode.") + })?, ) - .map_err(|_| Error::BadDatabase("Invalid Room ID in db."))?) + .map_err(|_| Error::bad_database("Room ID in userroomid_left is invalid."))?) }) } diff --git a/src/database/rooms/edus.rs b/src/database/rooms/edus.rs index c8b03aa9..645ccb0e 100644 --- a/src/database/rooms/edus.rs +++ b/src/database/rooms/edus.rs @@ -74,8 +74,9 @@ impl RoomEdus { .filter_map(|r| r.ok()) .take_while(move |(k, _)| k.starts_with(&prefix)) .map(|(_, v)| { - Ok(serde_json::from_slice(&v) - .map_err(|_| Error::BadDatabase("Read receipt in db is invalid."))?) + Ok(serde_json::from_slice(&v).map_err(|_| { + Error::bad_database("Read receipt in roomlatestid_roomlatest is invalid.") + })?) })) } @@ -164,10 +165,10 @@ impl RoomEdus { let key = key?; Ok::<_, Error>(( key.clone(), - utils::u64_from_bytes(key.split(|&b| b == 0xff).nth(1).ok_or( - Error::BadDatabase("RoomActive has invalid timestamp or delimiters."), - )?) - .map_err(|_| Error::BadDatabase("RoomActive has invalid timestamp bytes."))?, + utils::u64_from_bytes(key.split(|&b| b == 0xff).nth(1).ok_or_else(|| { + Error::bad_database("RoomActive has invalid timestamp or delimiters.") + })?) + .map_err(|_| Error::bad_database("RoomActive has invalid timestamp bytes."))?, )) }) .filter_map(|r| r.ok()) @@ -200,9 +201,9 @@ impl RoomEdus { .roomid_lastroomactiveupdate .get(&room_id.to_string().as_bytes())? .map_or(Ok::<_, Error>(None), |bytes| { - Ok(Some( - utils::u64_from_bytes(&bytes).map_err(|_| Error::BadDatabase(""))?, - )) + Ok(Some(utils::u64_from_bytes(&bytes).map_err(|_| { + Error::bad_database("Count in roomid_lastroomactiveupdate is invalid.") + })?)) })? .unwrap_or(0)) } @@ -219,9 +220,14 @@ impl RoomEdus { .scan_prefix(prefix) .values() .map(|user_id| { - Ok::<_, Error>(serde_json::from_slice(&user_id?).map_err(|_| { - Error::BadDatabase("User ID in roomactiveid_userid is invalid.") - })?) + Ok::<_, Error>( + UserId::try_from(utils::string_from_bytes(&user_id?).map_err(|_| { + Error::bad_database("User ID in roomactiveid_userid is invalid unicode.") + })?) + .map_err(|_| { + Error::bad_database("User ID in roomactiveid_userid is invalid.") + })?, + ) }) { user_ids.push(user_id?); @@ -252,7 +258,7 @@ impl RoomEdus { self.roomuserid_lastread.get(key)?.map_or(Ok(None), |v| { Ok(Some(utils::u64_from_bytes(&v).map_err(|_| { - Error::BadDatabase("Invalid private read marker bytes") + Error::bad_database("Invalid private read marker bytes") })?)) }) } diff --git a/src/database/uiaa.rs b/src/database/uiaa.rs index 0ae2ea43..fa4fc6ed 100644 --- a/src/database/uiaa.rs +++ b/src/database/uiaa.rs @@ -178,7 +178,7 @@ impl Uiaa { "UIAA session does not exist.", ))?, ) - .map_err(|_| Error::BadDatabase("UiaaInfo in userdeviceid_uiaainfo is invalid."))?; + .map_err(|_| Error::bad_database("UiaaInfo in userdeviceid_uiaainfo is invalid."))?; if uiaainfo .session diff --git a/src/database/users.rs b/src/database/users.rs index b70c2de4..07c69125 100644 --- a/src/database/users.rs +++ b/src/database/users.rs @@ -43,19 +43,22 @@ impl Users { .get(token)? .map_or(Ok(None), |bytes| { let mut parts = bytes.split(|&b| b == 0xff); - let user_bytes = parts.next().ok_or(Error::BadDatabase( - "token_userdeviceid value in db is invalid.", - ))?; - let device_bytes = parts.next().ok_or(Error::BadDatabase( - "token_userdeviceid value in db is invalid.", - ))?; + let user_bytes = parts.next().ok_or_else(|| { + Error::bad_database("User ID in token_userdeviceid is invalid.") + })?; + let device_bytes = parts.next().ok_or_else(|| { + Error::bad_database("Device ID in token_userdeviceid is invalid.") + })?; Ok(Some(( - serde_json::from_slice(&user_bytes).map_err(|_| { - Error::BadDatabase("User ID in token_userdeviceid is invalid.") + UserId::try_from(utils::string_from_bytes(&user_bytes).map_err(|_| { + Error::bad_database("User ID in token_userdeviceid is invalid unicode.") + })?) + .map_err(|_| { + Error::bad_database("User ID in token_userdeviceid is invalid.") })?, utils::string_from_bytes(&device_bytes).map_err(|_| { - Error::BadDatabase("Device ID in token_userdeviceid is invalid.") + Error::bad_database("Device ID in token_userdeviceid is invalid.") })?, ))) }) @@ -64,8 +67,12 @@ impl Users { /// Returns an iterator over all users on this homeserver. pub fn iter(&self) -> impl Iterator> { self.userid_password.iter().keys().map(|bytes| { - Ok(serde_json::from_slice(&bytes?) - .map_err(|_| Error::BadDatabase("User ID bytes in db are invalid."))?) + Ok( + UserId::try_from(utils::string_from_bytes(&bytes?).map_err(|_| { + Error::bad_database("User ID in userid_password is invalid unicode.") + })?) + .map_err(|_| Error::bad_database("User ID in userid_password is invalid."))?, + ) }) } @@ -75,7 +82,7 @@ impl Users { .get(user_id.to_string())? .map_or(Ok(None), |bytes| { Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| { - Error::BadDatabase("Password hash in db is not valid string.") + Error::bad_database("Password hash in db is not valid string.") })?)) }) } @@ -86,7 +93,7 @@ impl Users { .get(user_id.to_string())? .map_or(Ok(None), |bytes| { Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| { - Error::BadDatabase("Displayname in db is invalid.") + Error::bad_database("Displayname in db is invalid.") })?)) }) } @@ -109,7 +116,7 @@ impl Users { .get(user_id.to_string())? .map_or(Ok(None), |bytes| { Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| { - Error::BadDatabase("Avatar URL in db is invalid.") + Error::bad_database("Avatar URL in db is invalid.") })?)) }) } @@ -200,10 +207,10 @@ impl Users { &*bytes? .rsplit(|&b| b == 0xff) .next() - .ok_or(Error::BadDatabase("UserDevice ID in db is invalid."))?, + .ok_or_else(|| Error::bad_database("UserDevice ID in db is invalid."))?, ) .map_err(|_| { - Error::BadDatabase("Device ID in userdeviceid_metadata is invalid.") + Error::bad_database("Device ID in userdeviceid_metadata is invalid.") })?) }) } @@ -289,11 +296,11 @@ impl Users { &*key .rsplit(|&b| b == 0xff) .next() - .ok_or(Error::BadDatabase("OneTimeKeyId in db is invalid."))?, + .ok_or_else(|| Error::bad_database("OneTimeKeyId in db is invalid."))?, ) - .map_err(|_| Error::BadDatabase("OneTimeKeyId in db is invalid."))?, + .map_err(|_| Error::bad_database("OneTimeKeyId in db is invalid."))?, serde_json::from_slice(&*value) - .map_err(|_| Error::BadDatabase("OneTimeKeys in db are invalid."))?, + .map_err(|_| Error::bad_database("OneTimeKeys in db are invalid."))?, )) }) .transpose() @@ -317,12 +324,11 @@ impl Users { .map(|bytes| { Ok::<_, Error>( serde_json::from_slice::( - &*bytes? - .rsplit(|&b| b == 0xff) - .next() - .ok_or(Error::BadDatabase("OneTimeKey ID in db is invalid."))?, + &*bytes?.rsplit(|&b| b == 0xff).next().ok_or_else(|| { + Error::bad_database("OneTimeKey ID in db is invalid.") + })?, ) - .map_err(|_| Error::BadDatabase("AlgorithmAndDeviceID in db is invalid."))? + .map_err(|_| Error::bad_database("AlgorithmAndDeviceID in db is invalid."))? .0, ) }) @@ -369,7 +375,7 @@ impl Users { .values() .map(|bytes| { Ok(serde_json::from_slice(&bytes?) - .map_err(|_| Error::BadDatabase("DeviceKeys in db are invalid."))?) + .map_err(|_| Error::bad_database("DeviceKeys in db are invalid."))?) }) } @@ -378,9 +384,16 @@ impl Users { .range(since.to_be_bytes()..) .values() .map(|bytes| { - Ok(serde_json::from_slice(&bytes?).map_err(|_| { - Error::BadDatabase("User ID in devicekeychangeid_userid is invalid.") - })?) + Ok( + UserId::try_from(utils::string_from_bytes(&bytes?).map_err(|_| { + Error::bad_database( + "User ID in devicekeychangeid_userid is invalid unicode.", + ) + })?) + .map_err(|_| { + Error::bad_database("User ID in devicekeychangeid_userid is invalid.") + })?, + ) }) } @@ -396,13 +409,13 @@ impl Users { let userdeviceid = utils::string_from_bytes( key.rsplit(|&b| b == 0xff) .next() - .ok_or(Error::BadDatabase("UserDeviceID in db is invalid."))?, + .ok_or_else(|| Error::bad_database("UserDeviceID in db is invalid."))?, ) - .map_err(|_| Error::BadDatabase("UserDeviceId in db is invalid."))?; + .map_err(|_| Error::bad_database("UserDeviceId in db is invalid."))?; Ok(( userdeviceid, serde_json::from_slice(&*value) - .map_err(|_| Error::BadDatabase("DeviceKeys in db are invalid."))?, + .map_err(|_| Error::bad_database("DeviceKeys in db are invalid."))?, )) }) } @@ -452,7 +465,7 @@ impl Users { let (key, value) = result?; events.push( serde_json::from_slice(&*value) - .map_err(|_| Error::BadDatabase("Event in todeviceid_events is invalid."))?, + .map_err(|_| Error::bad_database("Event in todeviceid_events is invalid."))?, ); self.todeviceid_events.remove(key)?; } @@ -493,7 +506,7 @@ impl Users { .get(&userdeviceid)? .map_or(Ok(None), |bytes| { Ok(Some(serde_json::from_slice(&bytes).map_err(|_| { - Error::BadDatabase("Metadata in userdeviceid_metadata is invalid.") + Error::bad_database("Metadata in userdeviceid_metadata is invalid.") })?)) }) } @@ -507,7 +520,7 @@ impl Users { .values() .map(|bytes| { Ok(serde_json::from_slice::(&bytes?).map_err(|_| { - Error::BadDatabase("Device in userdeviceid_metadata is invalid.") + Error::bad_database("Device in userdeviceid_metadata is invalid.") })?) }) } diff --git a/src/error.rs b/src/error.rs index 3652f0a5..24bb39b4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,6 @@ use crate::RumaResponse; use http::StatusCode; +use log::error; use rocket::{ response::{self, Responder}, Request, @@ -27,6 +28,7 @@ pub enum Error { #[error("{0}")] BadConfig(&'static str), #[error("{0}")] + /// Don't create this directly. Use Error::bad_database instead. BadDatabase(&'static str), #[error("uiaa")] Uiaa(UiaaInfo), @@ -37,6 +39,13 @@ pub enum Error { Conflict(&'static str), // This is only needed for when a room alias already exists } +impl Error { + pub fn bad_database(message: &'static str) -> Self { + error!("BadDatabase: {}", message); + Self::BadDatabase(message) + } +} + #[rocket::async_trait] impl<'r> Responder<'r> for Error { async fn respond_to(self, r: &'r Request<'_>) -> response::Result<'r> { diff --git a/src/pdu.rs b/src/pdu.rs index 8c8423a6..5cb5fb1f 100644 --- a/src/pdu.rs +++ b/src/pdu.rs @@ -60,7 +60,7 @@ impl PduEvent { let old_content = self .content .as_object_mut() - .ok_or(Error::BadDatabase("PDU has invalid content"))?; + .ok_or_else(|| Error::bad_database("PDU in db has invalid content."))?; let mut new_content = serde_json::Map::new(); diff --git a/src/ruma_wrapper.rs b/src/ruma_wrapper.rs index 8be5c473..406357af 100644 --- a/src/ruma_wrapper.rs +++ b/src/ruma_wrapper.rs @@ -42,7 +42,10 @@ impl<'a, T: Endpoint> FromData<'a> for Ruma { let data = rocket::try_outcome!(outcome.owned()); let (user_id, device_id) = if T::METADATA.requires_authentication { - let db = request.guard::>().await.expect("database was loaded"); + let db = request + .guard::>() + .await + .expect("database was loaded"); // Get token from header or query value let token = match request diff --git a/sytest/sytest-whitelist b/sytest/sytest-whitelist index bf9059c7..a77c546f 100644 --- a/sytest/sytest-whitelist +++ b/sytest/sytest-whitelist @@ -35,8 +35,6 @@ POST /rooms/:room_id/invite can send an invite PUT /rooms/:room_id/state/m.room.power_levels can set levels PUT power_levels should not explode if the old power levels were empty Both GET and PUT work -Room creation reports m.room.create to myself -Room creation reports m.room.member to myself Version responds 200 OK with valid structure PUT /profile/:user_id/displayname sets my name GET /profile/:user_id/displayname publicly accessible @@ -78,3 +76,4 @@ User directory correctly update on display name change User in shared private room does appear in user directory User in dir while user still shares private rooms POST /rooms/:room_id/ban can ban a user +Alternative server names do not cause a routing loop