Merge branch 'uiaa' into 'master'
improvement: uiaa works like in synapse See merge request famedly/conduit!73next
commit
861cc76363
|
@ -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 New Issue