Merge branch 'uiaa' into 'master'
improvement: uiaa works like in synapse See merge request famedly/conduit!73
This commit is contained in:
		
						commit
						861cc76363
					
				
					 10 changed files with 326 additions and 177 deletions
				
			
		|  | @ -1,4 +1,7 @@ | |||
| use std::{collections::BTreeMap, convert::TryInto}; | ||||
| use std::{ | ||||
|     collections::BTreeMap, | ||||
|     convert::{TryFrom, TryInto}, | ||||
| }; | ||||
| 
 | ||||
| use super::{State, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH}; | ||||
| use crate::{pdu::PduBuilder, utils, ConduitResult, Database, Error, Ruma}; | ||||
|  | @ -143,16 +146,28 @@ pub async fn register_route( | |||
| 
 | ||||
|     if !body.from_appservice { | ||||
|         if let Some(auth) = &body.auth { | ||||
|             let (worked, uiaainfo) = | ||||
|                 db.uiaa | ||||
|                     .try_auth(&user_id, "".into(), auth, &uiaainfo, &db.users, &db.globals)?; | ||||
|             let (worked, uiaainfo) = db.uiaa.try_auth( | ||||
|                 &UserId::parse_with_server_name("", db.globals.server_name()) | ||||
|                     .expect("we know this is valid"), | ||||
|                 "".into(), | ||||
|                 auth, | ||||
|                 &uiaainfo, | ||||
|                 &db.users, | ||||
|                 &db.globals, | ||||
|             )?; | ||||
|             if !worked { | ||||
|                 return Err(Error::Uiaa(uiaainfo)); | ||||
|             } | ||||
|         // Success!
 | ||||
|         } else { | ||||
|             uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); | ||||
|             db.uiaa.create(&user_id, "".into(), &uiaainfo)?; | ||||
|             db.uiaa.create( | ||||
|                 &UserId::parse_with_server_name("", db.globals.server_name()) | ||||
|                     .expect("we know this is valid"), | ||||
|                 "".into(), | ||||
|                 &uiaainfo, | ||||
|                 &body.json_body.expect("body is json"), | ||||
|             )?; | ||||
|             return Err(Error::Uiaa(uiaainfo)); | ||||
|         } | ||||
|     } | ||||
|  | @ -526,7 +541,12 @@ pub async fn change_password_route( | |||
|     // Success!
 | ||||
|     } else { | ||||
|         uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); | ||||
|         db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?; | ||||
|         db.uiaa.create( | ||||
|             &sender_user, | ||||
|             &sender_device, | ||||
|             &uiaainfo, | ||||
|             &body.json_body.expect("body is json"), | ||||
|         )?; | ||||
|         return Err(Error::Uiaa(uiaainfo)); | ||||
|     } | ||||
| 
 | ||||
|  | @ -612,7 +632,12 @@ pub async fn deactivate_route( | |||
|     // Success!
 | ||||
|     } else { | ||||
|         uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); | ||||
|         db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?; | ||||
|         db.uiaa.create( | ||||
|             &sender_user, | ||||
|             &sender_device, | ||||
|             &uiaainfo, | ||||
|             &body.json_body.expect("body is json"), | ||||
|         )?; | ||||
|         return Err(Error::Uiaa(uiaainfo)); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -115,7 +115,12 @@ pub async fn delete_device_route( | |||
|     // Success!
 | ||||
|     } else { | ||||
|         uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); | ||||
|         db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?; | ||||
|         db.uiaa.create( | ||||
|             &sender_user, | ||||
|             &sender_device, | ||||
|             &uiaainfo, | ||||
|             &body.json_body.expect("body is json"), | ||||
|         )?; | ||||
|         return Err(Error::Uiaa(uiaainfo)); | ||||
|     } | ||||
| 
 | ||||
|  | @ -164,7 +169,12 @@ pub async fn delete_devices_route( | |||
|     // Success!
 | ||||
|     } else { | ||||
|         uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); | ||||
|         db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?; | ||||
|         db.uiaa.create( | ||||
|             &sender_user, | ||||
|             &sender_device, | ||||
|             &uiaainfo, | ||||
|             &body.json_body.expect("body is json"), | ||||
|         )?; | ||||
|         return Err(Error::Uiaa(uiaainfo)); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -203,19 +203,20 @@ pub async fn get_public_rooms_filtered_helper( | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     let mut all_rooms = | ||||
|         db.rooms | ||||
|             .public_rooms() | ||||
|             .map(|room_id| { | ||||
|                 let room_id = room_id?; | ||||
|     let mut all_rooms = db | ||||
|         .rooms | ||||
|         .public_rooms() | ||||
|         .map(|room_id| { | ||||
|             let room_id = room_id?; | ||||
| 
 | ||||
|                 let chunk = PublicRoomsChunk { | ||||
|                     aliases: Vec::new(), | ||||
|                     canonical_alias: db | ||||
|                         .rooms | ||||
|                         .room_state_get(&room_id, &EventType::RoomCanonicalAlias, "")? | ||||
|                         .map_or(Ok::<_, Error>(None), |s| { | ||||
|                             Ok(serde_json::from_value::< | ||||
|             let chunk = PublicRoomsChunk { | ||||
|                 aliases: Vec::new(), | ||||
|                 canonical_alias: db | ||||
|                     .rooms | ||||
|                     .room_state_get(&room_id, &EventType::RoomCanonicalAlias, "")? | ||||
|                     .map_or(Ok::<_, Error>(None), |s| { | ||||
|                         Ok( | ||||
|                             serde_json::from_value::< | ||||
|                                 Raw<canonical_alias::CanonicalAliasEventContent>, | ||||
|                             >(s.content) | ||||
|                             .expect("from_value::<Raw<..>> can never fail") | ||||
|  | @ -223,62 +224,61 @@ pub async fn get_public_rooms_filtered_helper( | |||
|                             .map_err(|_| { | ||||
|                                 Error::bad_database("Invalid canonical alias event in database.") | ||||
|                             })? | ||||
|                             .alias) | ||||
|                         })?, | ||||
|                     name: db | ||||
|                         .rooms | ||||
|                         .room_state_get(&room_id, &EventType::RoomName, "")? | ||||
|                         .map_or(Ok::<_, Error>(None), |s| { | ||||
|                             Ok(serde_json::from_value::<Raw<name::NameEventContent>>( | ||||
|                                 s.content, | ||||
|                             ) | ||||
|                             .expect("from_value::<Raw<..>> can never fail") | ||||
|                             .deserialize() | ||||
|                             .map_err(|_| { | ||||
|                                 Error::bad_database("Invalid room name event in database.") | ||||
|                             })? | ||||
|                             .name() | ||||
|                             .map(|n| n.to_owned())) | ||||
|                         })?, | ||||
|                     num_joined_members: (db.rooms.room_members(&room_id).count() as u32).into(), | ||||
|                     topic: db | ||||
|                         .rooms | ||||
|                         .room_state_get(&room_id, &EventType::RoomTopic, "")? | ||||
|                         .map_or(Ok::<_, Error>(None), |s| { | ||||
|                             Ok(Some( | ||||
|                                 serde_json::from_value::<Raw<topic::TopicEventContent>>( | ||||
|                                     s.content, | ||||
|                                 ) | ||||
|                             .alias, | ||||
|                         ) | ||||
|                     })?, | ||||
|                 name: db | ||||
|                     .rooms | ||||
|                     .room_state_get(&room_id, &EventType::RoomName, "")? | ||||
|                     .map_or(Ok::<_, Error>(None), |s| { | ||||
|                         Ok( | ||||
|                             serde_json::from_value::<Raw<name::NameEventContent>>(s.content) | ||||
|                                 .expect("from_value::<Raw<..>> can never fail") | ||||
|                                 .deserialize() | ||||
|                                 .map_err(|_| { | ||||
|                                     Error::bad_database("Invalid room name event in database.") | ||||
|                                 })? | ||||
|                                 .name() | ||||
|                                 .map(|n| n.to_owned()), | ||||
|                         ) | ||||
|                     })?, | ||||
|                 num_joined_members: (db.rooms.room_members(&room_id).count() as u32).into(), | ||||
|                 topic: db | ||||
|                     .rooms | ||||
|                     .room_state_get(&room_id, &EventType::RoomTopic, "")? | ||||
|                     .map_or(Ok::<_, Error>(None), |s| { | ||||
|                         Ok(Some( | ||||
|                             serde_json::from_value::<Raw<topic::TopicEventContent>>(s.content) | ||||
|                                 .expect("from_value::<Raw<..>> can never fail") | ||||
|                                 .deserialize() | ||||
|                                 .map_err(|_| { | ||||
|                                     Error::bad_database("Invalid room topic event in database.") | ||||
|                                 })? | ||||
|                                 .topic, | ||||
|                             )) | ||||
|                         })?, | ||||
|                     world_readable: db | ||||
|                         .rooms | ||||
|                         .room_state_get(&room_id, &EventType::RoomHistoryVisibility, "")? | ||||
|                         .map_or(Ok::<_, Error>(false), |s| { | ||||
|                             Ok(serde_json::from_value::< | ||||
|                                 Raw<history_visibility::HistoryVisibilityEventContent>, | ||||
|                             >(s.content) | ||||
|                             .expect("from_value::<Raw<..>> can never fail") | ||||
|                             .deserialize() | ||||
|                             .map_err(|_| { | ||||
|                                 Error::bad_database( | ||||
|                                     "Invalid room history visibility event in database.", | ||||
|                                 ) | ||||
|                             })? | ||||
|                             .history_visibility | ||||
|                                 == history_visibility::HistoryVisibility::WorldReadable) | ||||
|                         })?, | ||||
|                     guest_can_join: db | ||||
|                         .rooms | ||||
|                         .room_state_get(&room_id, &EventType::RoomGuestAccess, "")? | ||||
|                         .map_or(Ok::<_, Error>(false), |s| { | ||||
|                             Ok( | ||||
|                         )) | ||||
|                     })?, | ||||
|                 world_readable: db | ||||
|                     .rooms | ||||
|                     .room_state_get(&room_id, &EventType::RoomHistoryVisibility, "")? | ||||
|                     .map_or(Ok::<_, Error>(false), |s| { | ||||
|                         Ok(serde_json::from_value::< | ||||
|                             Raw<history_visibility::HistoryVisibilityEventContent>, | ||||
|                         >(s.content) | ||||
|                         .expect("from_value::<Raw<..>> can never fail") | ||||
|                         .deserialize() | ||||
|                         .map_err(|_| { | ||||
|                             Error::bad_database( | ||||
|                                 "Invalid room history visibility event in database.", | ||||
|                             ) | ||||
|                         })? | ||||
|                         .history_visibility | ||||
|                             == history_visibility::HistoryVisibility::WorldReadable) | ||||
|                     })?, | ||||
|                 guest_can_join: db | ||||
|                     .rooms | ||||
|                     .room_state_get(&room_id, &EventType::RoomGuestAccess, "")? | ||||
|                     .map_or(Ok::<_, Error>(false), |s| { | ||||
|                         Ok( | ||||
|                             serde_json::from_value::<Raw<guest_access::GuestAccessEventContent>>( | ||||
|                                 s.content, | ||||
|                             ) | ||||
|  | @ -290,33 +290,31 @@ pub async fn get_public_rooms_filtered_helper( | |||
|                             .guest_access | ||||
|                                 == guest_access::GuestAccess::CanJoin, | ||||
|                         ) | ||||
|                         })?, | ||||
|                     avatar_url: db | ||||
|                         .rooms | ||||
|                         .room_state_get(&room_id, &EventType::RoomAvatar, "")? | ||||
|                         .map(|s| { | ||||
|                             Ok::<_, Error>( | ||||
|                                 serde_json::from_value::<Raw<avatar::AvatarEventContent>>( | ||||
|                                     s.content, | ||||
|                                 ) | ||||
|                     })?, | ||||
|                 avatar_url: db | ||||
|                     .rooms | ||||
|                     .room_state_get(&room_id, &EventType::RoomAvatar, "")? | ||||
|                     .map(|s| { | ||||
|                         Ok::<_, Error>( | ||||
|                             serde_json::from_value::<Raw<avatar::AvatarEventContent>>(s.content) | ||||
|                                 .expect("from_value::<Raw<..>> can never fail") | ||||
|                                 .deserialize() | ||||
|                                 .map_err(|_| { | ||||
|                                     Error::bad_database("Invalid room avatar event in database.") | ||||
|                                 })? | ||||
|                                 .url, | ||||
|                             ) | ||||
|                         }) | ||||
|                         .transpose()? | ||||
|                         // url is now an Option<String> so we must flatten
 | ||||
|                         .flatten(), | ||||
|                     room_id, | ||||
|                 }; | ||||
|                 Ok(chunk) | ||||
|             }) | ||||
|             .filter_map(|r: Result<_>| r.ok()) // Filter out buggy rooms
 | ||||
|             // We need to collect all, so we can sort by member count
 | ||||
|             .collect::<Vec<_>>(); | ||||
|                         ) | ||||
|                     }) | ||||
|                     .transpose()? | ||||
|                     // url is now an Option<String> so we must flatten
 | ||||
|                     .flatten(), | ||||
|                 room_id, | ||||
|             }; | ||||
|             Ok(chunk) | ||||
|         }) | ||||
|         .filter_map(|r: Result<_>| r.ok()) // Filter out buggy rooms
 | ||||
|         // We need to collect all, so we can sort by member count
 | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     all_rooms.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members)); | ||||
| 
 | ||||
|  |  | |||
|  | @ -220,7 +220,12 @@ pub async fn upload_signing_keys_route( | |||
|     // Success!
 | ||||
|     } else { | ||||
|         uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); | ||||
|         db.uiaa.create(&sender_user, &sender_device, &uiaainfo)?; | ||||
|         db.uiaa.create( | ||||
|             &sender_user, | ||||
|             &sender_device, | ||||
|             &uiaainfo, | ||||
|             &body.json_body.expect("body is json"), | ||||
|         )?; | ||||
|         return Err(Error::Uiaa(uiaainfo)); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,13 +56,8 @@ pub async fn send_message_event_route( | |||
|     let event_id = db.rooms.build_and_append_pdu( | ||||
|         PduBuilder { | ||||
|             event_type: EventType::from(&body.event_type), | ||||
|             content: serde_json::from_str( | ||||
|                 body.json_body | ||||
|                     .as_ref() | ||||
|                     .ok_or(Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))? | ||||
|                     .get(), | ||||
|             ) | ||||
|             .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?, | ||||
|             content: serde_json::from_str(body.body.body.json().get()) | ||||
|                 .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?, | ||||
|             unsigned: Some(unsigned), | ||||
|             state_key: None, | ||||
|             redacts: None, | ||||
|  |  | |||
|  | @ -69,9 +69,9 @@ use { | |||
|     ruma::api::client::r0::to_device::send_event_to_device, | ||||
| }; | ||||
| 
 | ||||
| const DEVICE_ID_LENGTH: usize = 10; | ||||
| const TOKEN_LENGTH: usize = 256; | ||||
| const SESSION_ID_LENGTH: usize = 256; | ||||
| pub const DEVICE_ID_LENGTH: usize = 10; | ||||
| pub const TOKEN_LENGTH: usize = 256; | ||||
| pub const SESSION_ID_LENGTH: usize = 256; | ||||
| 
 | ||||
| #[cfg(feature = "conduit_bin")] | ||||
| #[options("/<_..>")] | ||||
|  |  | |||
|  | @ -135,7 +135,8 @@ impl Database { | |||
|                 todeviceid_events: db.open_tree("todeviceid_events")?, | ||||
|             }, | ||||
|             uiaa: uiaa::Uiaa { | ||||
|                 userdeviceid_uiaainfo: db.open_tree("userdeviceid_uiaainfo")?, | ||||
|                 userdevicesessionid_uiaainfo: db.open_tree("userdevicesessionid_uiaainfo")?, | ||||
|                 userdevicesessionid_uiaarequest: db.open_tree("userdevicesessionid_uiaarequest")?, | ||||
|             }, | ||||
|             rooms: rooms::Rooms { | ||||
|                 edus: rooms::RoomEdus { | ||||
|  |  | |||
|  | @ -1,15 +1,17 @@ | |||
| use crate::{Error, Result}; | ||||
| use crate::{client_server::SESSION_ID_LENGTH, utils, Error, Result}; | ||||
| use ruma::{ | ||||
|     api::client::{ | ||||
|         error::ErrorKind, | ||||
|         r0::uiaa::{IncomingAuthData, UiaaInfo}, | ||||
|     }, | ||||
|     signatures::CanonicalJsonValue, | ||||
|     DeviceId, UserId, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct Uiaa { | ||||
|     pub(super) userdeviceid_uiaainfo: sled::Tree, // User-interactive authentication
 | ||||
|     pub(super) userdevicesessionid_uiaainfo: sled::Tree, // User-interactive authentication
 | ||||
|     pub(super) userdevicesessionid_uiaarequest: sled::Tree, // UiaaRequest = canonical json value
 | ||||
| } | ||||
| 
 | ||||
| impl Uiaa { | ||||
|  | @ -19,8 +21,20 @@ impl Uiaa { | |||
|         user_id: &UserId, | ||||
|         device_id: &DeviceId, | ||||
|         uiaainfo: &UiaaInfo, | ||||
|         json_body: &CanonicalJsonValue, | ||||
|     ) -> Result<()> { | ||||
|         self.update_uiaa_session(user_id, device_id, Some(uiaainfo)) | ||||
|         self.set_uiaa_request( | ||||
|             user_id, | ||||
|             device_id, | ||||
|             uiaainfo.session.as_ref().expect("session should be set"), // TODO: better session error handling (why is it optional in ruma?)
 | ||||
|             json_body, | ||||
|         )?; | ||||
|         self.update_uiaa_session( | ||||
|             user_id, | ||||
|             device_id, | ||||
|             uiaainfo.session.as_ref().expect("session should be set"), | ||||
|             Some(uiaainfo), | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     pub fn try_auth( | ||||
|  | @ -45,6 +59,10 @@ impl Uiaa { | |||
|                 }) | ||||
|                 .unwrap_or_else(|| Ok(uiaainfo.clone()))?; | ||||
| 
 | ||||
|             if uiaainfo.session.is_none() { | ||||
|                 uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH)); | ||||
|             } | ||||
| 
 | ||||
|             // Find out what the user completed
 | ||||
|             match &**kind { | ||||
|                 "m.login.password" => { | ||||
|  | @ -130,35 +148,96 @@ impl Uiaa { | |||
|             } | ||||
| 
 | ||||
|             if !completed { | ||||
|                 self.update_uiaa_session(user_id, device_id, Some(&uiaainfo))?; | ||||
|                 self.update_uiaa_session( | ||||
|                     user_id, | ||||
|                     device_id, | ||||
|                     uiaainfo.session.as_ref().expect("session is always set"), | ||||
|                     Some(&uiaainfo), | ||||
|                 )?; | ||||
|                 return Ok((false, uiaainfo)); | ||||
|             } | ||||
| 
 | ||||
|             // UIAA was successful! Remove this session and return true
 | ||||
|             self.update_uiaa_session(user_id, device_id, None)?; | ||||
|             self.update_uiaa_session( | ||||
|                 user_id, | ||||
|                 device_id, | ||||
|                 uiaainfo.session.as_ref().expect("session is always set"), | ||||
|                 None, | ||||
|             )?; | ||||
|             Ok((true, uiaainfo)) | ||||
|         } else { | ||||
|             panic!("FallbackAcknowledgement is not supported yet"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn set_uiaa_request( | ||||
|         &self, | ||||
|         user_id: &UserId, | ||||
|         device_id: &DeviceId, | ||||
|         session: &str, | ||||
|         request: &CanonicalJsonValue, | ||||
|     ) -> Result<()> { | ||||
|         let mut userdevicesessionid = user_id.as_bytes().to_vec(); | ||||
|         userdevicesessionid.push(0xff); | ||||
|         userdevicesessionid.extend_from_slice(device_id.as_bytes()); | ||||
|         userdevicesessionid.push(0xff); | ||||
|         userdevicesessionid.extend_from_slice(session.as_bytes()); | ||||
| 
 | ||||
|         self.userdevicesessionid_uiaarequest.insert( | ||||
|             &userdevicesessionid, | ||||
|             &*serde_json::to_string(request).expect("json value to string always works"), | ||||
|         )?; | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_uiaa_request( | ||||
|         &self, | ||||
|         user_id: &UserId, | ||||
|         device_id: &DeviceId, | ||||
|         session: &str, | ||||
|     ) -> Result<Option<CanonicalJsonValue>> { | ||||
|         let mut userdevicesessionid = user_id.as_bytes().to_vec(); | ||||
|         userdevicesessionid.push(0xff); | ||||
|         userdevicesessionid.extend_from_slice(device_id.as_bytes()); | ||||
|         userdevicesessionid.push(0xff); | ||||
|         userdevicesessionid.extend_from_slice(session.as_bytes()); | ||||
| 
 | ||||
|         self.userdevicesessionid_uiaarequest | ||||
|             .get(&userdevicesessionid)? | ||||
|             .map_or(Ok(None), |bytes| { | ||||
|                 Ok::<_, Error>(Some( | ||||
|                     serde_json::from_str::<CanonicalJsonValue>( | ||||
|                         &utils::string_from_bytes(&bytes).map_err(|_| { | ||||
|                             Error::bad_database("Invalid uiaa request bytes in db.") | ||||
|                         })?, | ||||
|                     ) | ||||
|                     .map_err(|_| Error::bad_database("Invalid uiaa request in db."))?, | ||||
|                 )) | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     fn update_uiaa_session( | ||||
|         &self, | ||||
|         user_id: &UserId, | ||||
|         device_id: &DeviceId, | ||||
|         session: &str, | ||||
|         uiaainfo: Option<&UiaaInfo>, | ||||
|     ) -> Result<()> { | ||||
|         let mut userdeviceid = user_id.as_bytes().to_vec(); | ||||
|         userdeviceid.push(0xff); | ||||
|         userdeviceid.extend_from_slice(device_id.as_bytes()); | ||||
|         let mut userdevicesessionid = user_id.as_bytes().to_vec(); | ||||
|         userdevicesessionid.push(0xff); | ||||
|         userdevicesessionid.extend_from_slice(device_id.as_bytes()); | ||||
|         userdevicesessionid.push(0xff); | ||||
|         userdevicesessionid.extend_from_slice(session.as_bytes()); | ||||
| 
 | ||||
|         if let Some(uiaainfo) = uiaainfo { | ||||
|             self.userdeviceid_uiaainfo.insert( | ||||
|                 &userdeviceid, | ||||
|             self.userdevicesessionid_uiaainfo.insert( | ||||
|                 &userdevicesessionid, | ||||
|                 &*serde_json::to_string(&uiaainfo).expect("UiaaInfo::to_string always works"), | ||||
|             )?; | ||||
|         } else { | ||||
|             self.userdeviceid_uiaainfo.remove(&userdeviceid)?; | ||||
|             self.userdevicesessionid_uiaainfo | ||||
|                 .remove(&userdevicesessionid)?; | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|  | @ -170,14 +249,16 @@ impl Uiaa { | |||
|         device_id: &DeviceId, | ||||
|         session: &str, | ||||
|     ) -> Result<UiaaInfo> { | ||||
|         let mut userdeviceid = user_id.as_bytes().to_vec(); | ||||
|         userdeviceid.push(0xff); | ||||
|         userdeviceid.extend_from_slice(device_id.as_bytes()); | ||||
|         let mut userdevicesessionid = user_id.as_bytes().to_vec(); | ||||
|         userdevicesessionid.push(0xff); | ||||
|         userdevicesessionid.extend_from_slice(device_id.as_bytes()); | ||||
|         userdevicesessionid.push(0xff); | ||||
|         userdevicesessionid.extend_from_slice(session.as_bytes()); | ||||
| 
 | ||||
|         let uiaainfo = serde_json::from_slice::<UiaaInfo>( | ||||
|             &self | ||||
|                 .userdeviceid_uiaainfo | ||||
|                 .get(&userdeviceid)? | ||||
|                 .userdevicesessionid_uiaainfo | ||||
|                 .get(&userdevicesessionid)? | ||||
|                 .ok_or(Error::BadRequest( | ||||
|                     ErrorKind::Forbidden, | ||||
|                     "UIAA session does not exist.", | ||||
|  | @ -185,18 +266,6 @@ impl Uiaa { | |||
|         ) | ||||
|         .map_err(|_| Error::bad_database("UiaaInfo in userdeviceid_uiaainfo is invalid."))?; | ||||
| 
 | ||||
|         if uiaainfo | ||||
|             .session | ||||
|             .as_ref() | ||||
|             .filter(|&s| s == session) | ||||
|             .is_none() | ||||
|         { | ||||
|             return Err(Error::BadRequest( | ||||
|                 ErrorKind::Forbidden, | ||||
|                 "UIAA session token invalid.", | ||||
|             )); | ||||
|         } | ||||
| 
 | ||||
|         Ok(uiaainfo) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ use std::ops::Deref; | |||
| 
 | ||||
| #[cfg(feature = "conduit_bin")] | ||||
| use { | ||||
|     crate::{server_server, utils}, | ||||
|     crate::server_server, | ||||
|     log::{debug, warn}, | ||||
|     rocket::{ | ||||
|         data::{self, ByteUnit, Data, FromData}, | ||||
|  | @ -35,7 +35,7 @@ pub struct Ruma<T: Outgoing> { | |||
|     pub sender_user: Option<UserId>, | ||||
|     pub sender_device: Option<Box<DeviceId>>, | ||||
|     // This is None when body is not a valid string
 | ||||
|     pub json_body: Option<Box<serde_json::value::RawValue>>, | ||||
|     pub json_body: Option<CanonicalJsonValue>, | ||||
|     pub from_appservice: bool, | ||||
| } | ||||
| 
 | ||||
|  | @ -66,6 +66,8 @@ where | |||
|         let mut body = Vec::new(); | ||||
|         handle.read_to_end(&mut body).await.unwrap(); | ||||
| 
 | ||||
|         let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&body).ok(); | ||||
| 
 | ||||
|         let (sender_user, sender_device, from_appservice) = if let Some((_id, registration)) = db | ||||
|             .appservice | ||||
|             .iter_all() | ||||
|  | @ -115,7 +117,7 @@ where | |||
|                             // Unknown Token
 | ||||
|                             None => return Failure((Status::raw(581), ())), | ||||
|                             Some((user_id, device_id)) => { | ||||
|                                 (Some(user_id), Some(device_id.into()), false) | ||||
|                                 (Some(user_id), Some(Box::<DeviceId>::from(device_id)), false) | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|  | @ -187,12 +189,10 @@ where | |||
|                         } | ||||
|                     }; | ||||
| 
 | ||||
|                     let json_body = serde_json::from_slice::<CanonicalJsonValue>(&body); | ||||
| 
 | ||||
|                     let mut request_map = BTreeMap::<String, CanonicalJsonValue>::new(); | ||||
| 
 | ||||
|                     if let Ok(json_body) = json_body { | ||||
|                         request_map.insert("content".to_owned(), json_body); | ||||
|                     if let Some(json_body) = &json_body { | ||||
|                         request_map.insert("content".to_owned(), json_body.clone()); | ||||
|                     }; | ||||
| 
 | ||||
|                     request_map.insert( | ||||
|  | @ -271,6 +271,43 @@ where | |||
|             http_request = http_request.header(header.name.as_str(), &*header.value); | ||||
|         } | ||||
| 
 | ||||
|         match &mut json_body { | ||||
|             Some(CanonicalJsonValue::Object(json_body)) => { | ||||
|                 let user_id = sender_user.clone().unwrap_or_else(|| { | ||||
|                     UserId::parse_with_server_name("", db.globals.server_name()) | ||||
|                         .expect("we know this is valid") | ||||
|                 }); | ||||
| 
 | ||||
|                 if let Some(initial_request) = json_body | ||||
|                     .get("auth") | ||||
|                     .and_then(|auth| auth.as_object()) | ||||
|                     .and_then(|auth| auth.get("session")) | ||||
|                     .and_then(|session| session.as_str()) | ||||
|                     .and_then(|session| { | ||||
|                         db.uiaa | ||||
|                             .get_uiaa_request( | ||||
|                                 &user_id, | ||||
|                                 &sender_device.clone().unwrap_or_else(|| "".into()), | ||||
|                                 session, | ||||
|                             ) | ||||
|                             .ok() | ||||
|                             .flatten() | ||||
|                     }) | ||||
|                 { | ||||
|                     match initial_request { | ||||
|                         CanonicalJsonValue::Object(initial_request) => { | ||||
|                             for (key, value) in initial_request.into_iter() { | ||||
|                                 json_body.entry(key).or_insert(value); | ||||
|                             } | ||||
|                         } | ||||
|                         _ => {} | ||||
|                     } | ||||
|                 } | ||||
|                 body = serde_json::to_vec(json_body).expect("value to bytes can't fail"); | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
| 
 | ||||
|         let http_request = http_request.body(&*body).unwrap(); | ||||
|         debug!("{:?}", http_request); | ||||
|         match <T::Incoming as IncomingRequest>::try_from_http_request(http_request) { | ||||
|  | @ -278,11 +315,8 @@ where | |||
|                 body: t, | ||||
|                 sender_user, | ||||
|                 sender_device, | ||||
|                 // TODO: Can we avoid parsing it again? (We only need this for append_pdu)
 | ||||
|                 json_body: utils::string_from_bytes(&body) | ||||
|                     .ok() | ||||
|                     .and_then(|s| serde_json::value::RawValue::from_string(s).ok()), | ||||
|                 from_appservice, | ||||
|                 json_body, | ||||
|             }), | ||||
|             Err(e) => { | ||||
|                 warn!("{:?}", e); | ||||
|  |  | |||
|  | @ -1018,29 +1018,6 @@ pub fn handle_incoming_pdu<'a>( | |||
|         } | ||||
|         debug!("Auth check succeeded."); | ||||
| 
 | ||||
|         // 13. Check if the event passes auth based on the "current state" of the room, if not "soft fail" it
 | ||||
|         let current_state = db | ||||
|             .rooms | ||||
|             .room_state_full(&room_id) | ||||
|             .map_err(|_| "Failed to load room state.".to_owned())? | ||||
|             .into_iter() | ||||
|             .map(|(k, v)| (k, Arc::new(v))) | ||||
|             .collect(); | ||||
| 
 | ||||
|         if !state_res::event_auth::auth_check( | ||||
|             &room_version, | ||||
|             &incoming_pdu, | ||||
|             previous_create, | ||||
|             ¤t_state, | ||||
|             None, | ||||
|         ) | ||||
|         .map_err(|_e| "Auth check failed.".to_owned())? | ||||
|         { | ||||
|             // Soft fail, we leave the event as an outlier but don't add it to the timeline
 | ||||
|             return Err("Event has been soft failed".into()); | ||||
|         }; | ||||
|         debug!("Auth check with current state succeeded."); | ||||
| 
 | ||||
|         // Now we calculate the set of extremities this room has after the incoming event has been
 | ||||
|         // applied. We start with the previous extremities (aka leaves)
 | ||||
|         let mut extremities = db | ||||
|  | @ -1103,6 +1080,14 @@ pub fn handle_incoming_pdu<'a>( | |||
|         //     don't just trust a set of state we got from a remote).
 | ||||
| 
 | ||||
|         // We do this by adding the current state to the list of fork states
 | ||||
|         let current_state = db | ||||
|             .rooms | ||||
|             .room_state_full(&room_id) | ||||
|             .map_err(|_| "Failed to load room state.".to_owned())? | ||||
|             .into_iter() | ||||
|             .map(|(k, v)| (k, Arc::new(v))) | ||||
|             .collect(); | ||||
| 
 | ||||
|         fork_states.insert(current_state); | ||||
| 
 | ||||
|         // We also add state after incoming event to the fork states
 | ||||
|  | @ -1199,18 +1184,40 @@ pub fn handle_incoming_pdu<'a>( | |||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         // Now that the event has passed all auth it is added into the timeline.
 | ||||
|         // We use the `state_at_event` instead of `state_after` so we accurately
 | ||||
|         // represent the state for this event.
 | ||||
|         let pdu_id = append_incoming_pdu( | ||||
|             &db, | ||||
|         // 13. Check if the event passes auth based on the "current state" of the room, if not "soft fail" it
 | ||||
|         let soft_fail = !state_res::event_auth::auth_check( | ||||
|             &room_version, | ||||
|             &incoming_pdu, | ||||
|             val, | ||||
|             extremities, | ||||
|             &state_at_incoming_event, | ||||
|             previous_create, | ||||
|             &new_room_state | ||||
|                 .iter() | ||||
|                 .filter_map(|(k, v)| { | ||||
|                     Some((k.clone(), Arc::new(db.rooms.get_pdu(&v).ok().flatten()?))) | ||||
|                 }) | ||||
|                 .collect(), | ||||
|             None, | ||||
|         ) | ||||
|         .map_err(|_| "Failed to add pdu to db.".to_owned())?; | ||||
|         debug!("Appended incoming pdu."); | ||||
|         .map_err(|_e| "Auth check failed.".to_owned())?; | ||||
| 
 | ||||
|         let mut pdu_id = None; | ||||
|         if !soft_fail { | ||||
|             // Now that the event has passed all auth it is added into the timeline.
 | ||||
|             // We use the `state_at_event` instead of `state_after` so we accurately
 | ||||
|             // represent the state for this event.
 | ||||
|             pdu_id = Some( | ||||
|                 append_incoming_pdu( | ||||
|                     &db, | ||||
|                     &incoming_pdu, | ||||
|                     val, | ||||
|                     extremities, | ||||
|                     &state_at_incoming_event, | ||||
|                 ) | ||||
|                 .map_err(|_| "Failed to add pdu to db.".to_owned())?, | ||||
|             ); | ||||
|             debug!("Appended incoming pdu."); | ||||
|         } else { | ||||
|             warn!("Event was soft failed: {:?}", incoming_pdu); | ||||
|         } | ||||
| 
 | ||||
|         // Set the new room state to the resolved state
 | ||||
|         if update_state { | ||||
|  | @ -1220,8 +1227,13 @@ pub fn handle_incoming_pdu<'a>( | |||
|         } | ||||
|         debug!("Updated resolved state"); | ||||
| 
 | ||||
|         if soft_fail { | ||||
|             // Soft fail, we leave the event as an outlier but don't add it to the timeline
 | ||||
|             return Err("Event has been soft failed".into()); | ||||
|         } | ||||
| 
 | ||||
|         // Event has passed all auth/stateres checks
 | ||||
|         Ok(Some(pdu_id)) | ||||
|         Ok(pdu_id) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue