refactor: better error handling

next
timokoesters 2020-06-09 15:13:17 +02:00
parent 80935c5826
commit 2368a90584
No known key found for this signature in database
GPG Key ID: 24DA7517711A2BA4
15 changed files with 2062 additions and 1909 deletions

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ pub(self) mod rooms;
pub(self) mod uiaa; pub(self) mod uiaa;
pub(self) mod users; pub(self) mod users;
use crate::{Error, Result};
use directories::ProjectDirs; use directories::ProjectDirs;
use log::info; use log::info;
use std::fs::remove_dir_all; use std::fs::remove_dir_all;
@ -25,84 +26,92 @@ pub struct Database {
impl Database { impl Database {
/// Tries to remove the old database but ignores all errors. /// Tries to remove the old database but ignores all errors.
pub fn try_remove(server_name: &str) { pub fn try_remove(server_name: &str) -> Result<()> {
let mut path = ProjectDirs::from("xyz", "koesters", "conduit") let mut path = ProjectDirs::from("xyz", "koesters", "conduit")
.unwrap() .ok_or(Error::BadConfig(
"The OS didn't return a valid home directory path.",
))?
.data_dir() .data_dir()
.to_path_buf(); .to_path_buf();
path.push(server_name); path.push(server_name);
let _ = remove_dir_all(path); let _ = remove_dir_all(path);
Ok(())
} }
/// Load an existing database or create a new one. /// Load an existing database or create a new one.
pub fn load_or_create(config: &Config) -> Self { pub fn load_or_create(config: &Config) -> Result<Self> {
let server_name = config.get_str("server_name").unwrap_or("localhost"); let server_name = config.get_str("server_name").unwrap_or("localhost");
let path = config let path = config
.get_str("database_path") .get_str("database_path")
.map(|x| x.to_owned()) .map(|x| Ok::<_, Error>(x.to_owned()))
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
let path = ProjectDirs::from("xyz", "koesters", "conduit") let path = ProjectDirs::from("xyz", "koesters", "conduit")
.unwrap() .ok_or(Error::BadConfig(
"The OS didn't return a valid home directory path.",
))?
.data_dir() .data_dir()
.join(server_name); .join(server_name);
path.to_str().unwrap().to_owned()
});
let db = sled::open(&path).unwrap(); Ok(path
.to_str()
.ok_or(Error::BadConfig("Database path contains invalid unicode."))?
.to_owned())
})?;
let db = sled::open(&path)?;
info!("Opened sled database at {}", path); info!("Opened sled database at {}", path);
Self { Ok(Self {
globals: globals::Globals::load(db.open_tree("global").unwrap(), config), globals: globals::Globals::load(db.open_tree("global")?, config)?,
users: users::Users { users: users::Users {
userid_password: db.open_tree("userid_password").unwrap(), userid_password: db.open_tree("userid_password")?,
userid_displayname: db.open_tree("userid_displayname").unwrap(), userid_displayname: db.open_tree("userid_displayname")?,
userid_avatarurl: db.open_tree("userid_avatarurl").unwrap(), userid_avatarurl: db.open_tree("userid_avatarurl")?,
userdeviceid_token: db.open_tree("userdeviceid_token").unwrap(), userdeviceid_token: db.open_tree("userdeviceid_token")?,
userdeviceid_metadata: db.open_tree("userdeviceid_metadata").unwrap(), userdeviceid_metadata: db.open_tree("userdeviceid_metadata")?,
token_userdeviceid: db.open_tree("token_userdeviceid").unwrap(), token_userdeviceid: db.open_tree("token_userdeviceid")?,
onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys").unwrap(), onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys")?,
userdeviceid_devicekeys: db.open_tree("userdeviceid_devicekeys").unwrap(), userdeviceid_devicekeys: db.open_tree("userdeviceid_devicekeys")?,
devicekeychangeid_userid: db.open_tree("devicekeychangeid_userid").unwrap(), devicekeychangeid_userid: db.open_tree("devicekeychangeid_userid")?,
todeviceid_events: db.open_tree("todeviceid_events").unwrap(), todeviceid_events: db.open_tree("todeviceid_events")?,
}, },
uiaa: uiaa::Uiaa { uiaa: uiaa::Uiaa {
userdeviceid_uiaainfo: db.open_tree("userdeviceid_uiaainfo").unwrap(), userdeviceid_uiaainfo: db.open_tree("userdeviceid_uiaainfo")?,
}, },
rooms: rooms::Rooms { rooms: rooms::Rooms {
edus: rooms::RoomEdus { edus: rooms::RoomEdus {
roomuserid_lastread: db.open_tree("roomuserid_lastread").unwrap(), // "Private" read receipt roomuserid_lastread: db.open_tree("roomuserid_lastread")?, // "Private" read receipt
roomlatestid_roomlatest: db.open_tree("roomlatestid_roomlatest").unwrap(), // Read receipts roomlatestid_roomlatest: db.open_tree("roomlatestid_roomlatest")?, // Read receipts
roomactiveid_userid: db.open_tree("roomactiveid_userid").unwrap(), // Typing notifs roomactiveid_userid: db.open_tree("roomactiveid_userid")?, // Typing notifs
roomid_lastroomactiveupdate: db roomid_lastroomactiveupdate: db.open_tree("roomid_lastroomactiveupdate")?,
.open_tree("roomid_lastroomactiveupdate")
.unwrap(),
}, },
pduid_pdu: db.open_tree("pduid_pdu").unwrap(), pduid_pdu: db.open_tree("pduid_pdu")?,
eventid_pduid: db.open_tree("eventid_pduid").unwrap(), eventid_pduid: db.open_tree("eventid_pduid")?,
roomid_pduleaves: db.open_tree("roomid_pduleaves").unwrap(), roomid_pduleaves: db.open_tree("roomid_pduleaves")?,
roomstateid_pdu: db.open_tree("roomstateid_pdu").unwrap(), roomstateid_pdu: db.open_tree("roomstateid_pdu")?,
alias_roomid: db.open_tree("alias_roomid").unwrap(), alias_roomid: db.open_tree("alias_roomid")?,
aliasid_alias: db.open_tree("alias_roomid").unwrap(), aliasid_alias: db.open_tree("alias_roomid")?,
publicroomids: db.open_tree("publicroomids").unwrap(), publicroomids: db.open_tree("publicroomids")?,
userroomid_joined: db.open_tree("userroomid_joined").unwrap(), userroomid_joined: db.open_tree("userroomid_joined")?,
roomuserid_joined: db.open_tree("roomuserid_joined").unwrap(), roomuserid_joined: db.open_tree("roomuserid_joined")?,
userroomid_invited: db.open_tree("userroomid_invited").unwrap(), userroomid_invited: db.open_tree("userroomid_invited")?,
roomuserid_invited: db.open_tree("roomuserid_invited").unwrap(), roomuserid_invited: db.open_tree("roomuserid_invited")?,
userroomid_left: db.open_tree("userroomid_left").unwrap(), userroomid_left: db.open_tree("userroomid_left")?,
}, },
account_data: account_data::AccountData { account_data: account_data::AccountData {
roomuserdataid_accountdata: db.open_tree("roomuserdataid_accountdata").unwrap(), roomuserdataid_accountdata: db.open_tree("roomuserdataid_accountdata")?,
}, },
global_edus: global_edus::GlobalEdus { global_edus: global_edus::GlobalEdus {
presenceid_presence: db.open_tree("presenceid_presence").unwrap(), // Presence presenceid_presence: db.open_tree("presenceid_presence")?, // Presence
}, },
media: media::Media { media: media::Media {
mediaid_file: db.open_tree("mediaid_file").unwrap(), mediaid_file: db.open_tree("mediaid_file")?,
}, },
_db: db, _db: db,
} })
} }
} }

View File

@ -1,5 +1,6 @@
use crate::{utils, Error, Result}; use crate::{utils, Error, Result};
use ruma::{ use ruma::{
api::client::error::ErrorKind,
events::{collections::only::Event as EduEvent, EventJson, EventType}, events::{collections::only::Event as EduEvent, EventJson, EventType},
identifiers::{RoomId, UserId}, identifiers::{RoomId, UserId},
}; };
@ -20,7 +21,10 @@ impl AccountData {
globals: &super::globals::Globals, globals: &super::globals::Globals,
) -> Result<()> { ) -> Result<()> {
if json.get("content").is_none() { if json.get("content").is_none() {
return Err(Error::BadRequest("json needs to have a content field")); return Err(Error::BadRequest(
ErrorKind::BadJson,
"Json needs to have a content field.",
));
} }
json.insert("type".to_owned(), kind.to_string().into()); json.insert("type".to_owned(), kind.to_string().into());
@ -62,9 +66,10 @@ impl AccountData {
key.push(0xff); key.push(0xff);
key.extend_from_slice(kind.to_string().as_bytes()); key.extend_from_slice(kind.to_string().as_bytes());
self.roomuserdataid_accountdata self.roomuserdataid_accountdata.insert(
.insert(key, &*serde_json::to_string(&json)?) key,
.unwrap(); &*serde_json::to_string(&json).expect("Map::to_string always works"),
)?;
Ok(()) Ok(())
} }
@ -109,17 +114,22 @@ impl AccountData {
.take_while(move |(k, _)| k.starts_with(&prefix)) .take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(k, v)| { .map(|(k, v)| {
Ok::<_, Error>(( Ok::<_, Error>((
EventType::try_from(utils::string_from_bytes( EventType::try_from(
utils::string_from_bytes(
k.rsplit(|&b| b == 0xff) k.rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("roomuserdataid is invalid"))?, .ok_or(Error::BadDatabase("RoomUserData ID in db is invalid."))?,
)?) )
.map_err(|_| Error::BadDatabase("roomuserdataid is invalid"))?, .map_err(|_| Error::BadDatabase("RoomUserData ID in db is invalid."))?,
serde_json::from_slice::<EventJson<EduEvent>>(&v).unwrap(), )
.map_err(|_| Error::BadDatabase("RoomUserData ID in db is invalid."))?,
serde_json::from_slice::<EventJson<EduEvent>>(&v).map_err(|_| {
Error::BadDatabase("Database contains invalid account data.")
})?,
)) ))
}) })
{ {
let (kind, data) = r.unwrap(); let (kind, data) = r?;
userdata.insert(kind, data); userdata.insert(kind, data);
} }

View File

@ -1,4 +1,4 @@
use crate::Result; use crate::{Error, Result};
use ruma::events::EventJson; use ruma::events::EventJson;
pub struct GlobalEdus { pub struct GlobalEdus {
@ -21,7 +21,10 @@ impl GlobalEdus {
.rev() .rev()
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.find(|key| { .find(|key| {
key.rsplit(|&b| b == 0xff).next().unwrap() == presence.sender.to_string().as_bytes() key.rsplit(|&b| b == 0xff)
.next()
.expect("rsplit always returns an element")
== presence.sender.to_string().as_bytes()
}) })
{ {
// This is the old global_latest // This is the old global_latest
@ -32,8 +35,10 @@ impl GlobalEdus {
presence_id.push(0xff); presence_id.push(0xff);
presence_id.extend_from_slice(&presence.sender.to_string().as_bytes()); presence_id.extend_from_slice(&presence.sender.to_string().as_bytes());
self.presenceid_presence self.presenceid_presence.insert(
.insert(presence_id, &*serde_json::to_string(&presence)?)?; presence_id,
&*serde_json::to_string(&presence).expect("PresenceEvent can be serialized"),
)?;
Ok(()) Ok(())
} }
@ -50,6 +55,9 @@ impl GlobalEdus {
.presenceid_presence .presenceid_presence
.range(&*first_possible_edu..) .range(&*first_possible_edu..)
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))) .map(|(_, v)| {
Ok(serde_json::from_slice(&v)
.map_err(|_| Error::BadDatabase("Invalid presence event in db."))?)
}))
} }
} }

View File

@ -1,4 +1,4 @@
use crate::{utils, Result}; use crate::{utils, Error, Result};
pub const COUNTER: &str = "c"; pub const COUNTER: &str = "c";
@ -11,17 +11,16 @@ pub struct Globals {
} }
impl Globals { impl Globals {
pub fn load(globals: sled::Tree, config: &rocket::Config) -> Self { pub fn load(globals: sled::Tree, config: &rocket::Config) -> Result<Self> {
let keypair = ruma::signatures::Ed25519KeyPair::new( let keypair = ruma::signatures::Ed25519KeyPair::new(
&*globals &*globals
.update_and_fetch("keypair", utils::generate_keypair) .update_and_fetch("keypair", utils::generate_keypair)?
.unwrap() .expect("utils::generate_keypair always returns Some"),
.unwrap(),
"key1".to_owned(), "key1".to_owned(),
) )
.unwrap(); .map_err(|_| Error::BadDatabase("Private or public keys are invalid."))?;
Self { Ok(Self {
globals, globals,
keypair, keypair,
reqwest_client: reqwest::Client::new(), reqwest_client: reqwest::Client::new(),
@ -30,7 +29,7 @@ impl Globals {
.unwrap_or("localhost") .unwrap_or("localhost")
.to_owned(), .to_owned(),
registration_disabled: config.get_bool("registration_disabled").unwrap_or(false), registration_disabled: config.get_bool("registration_disabled").unwrap_or(false),
} })
} }
/// Returns this server's keypair. /// Returns this server's keypair.
@ -49,14 +48,15 @@ impl Globals {
.globals .globals
.update_and_fetch(COUNTER, utils::increment)? .update_and_fetch(COUNTER, utils::increment)?
.expect("utils::increment will always put in a value"), .expect("utils::increment will always put in a value"),
)) )
.map_err(|_| Error::BadDatabase("Count has invalid bytes."))?)
} }
pub fn current_count(&self) -> Result<u64> { pub fn current_count(&self) -> Result<u64> {
Ok(self self.globals.get(COUNTER)?.map_or(Ok(0_u64), |bytes| {
.globals Ok(utils::u64_from_bytes(&bytes)
.get(COUNTER)? .map_err(|_| Error::BadDatabase("Count has invalid bytes."))?)
.map_or(0_u64, |bytes| utils::u64_from_bytes(&bytes))) })
} }
pub fn server_name(&self) -> &str { pub fn server_name(&self) -> &str {

View File

@ -43,16 +43,21 @@ impl Media {
let content_type = utils::string_from_bytes( let content_type = utils::string_from_bytes(
parts parts
.next() .next()
.ok_or(Error::BadDatabase("mediaid is invalid"))?, .ok_or(Error::BadDatabase("Invalid Media ID in db"))?,
)?; )
.map_err(|_| Error::BadDatabase("Invalid content type in db."))?;
let filename_bytes = parts let filename_bytes = parts
.next() .next()
.ok_or(Error::BadDatabase("mediaid is invalid"))?; .ok_or(Error::BadDatabase("Media ID in db is invalid."))?;
let filename = if filename_bytes.is_empty() { let filename = if filename_bytes.is_empty() {
None None
} else { } else {
Some(utils::string_from_bytes(filename_bytes)?) Some(
utils::string_from_bytes(filename_bytes)
.map_err(|_| Error::BadDatabase("Filename in db is invalid."))?,
)
}; };
Ok(Some((filename, content_type, file.to_vec()))) Ok(Some((filename, content_type, file.to_vec())))
@ -89,16 +94,21 @@ impl Media {
let content_type = utils::string_from_bytes( let content_type = utils::string_from_bytes(
parts parts
.next() .next()
.ok_or(Error::BadDatabase("mediaid is invalid"))?, .ok_or(Error::BadDatabase("Invalid Media ID in db"))?,
)?; )
.map_err(|_| Error::BadDatabase("Invalid content type in db."))?;
let filename_bytes = parts let filename_bytes = parts
.next() .next()
.ok_or(Error::BadDatabase("mediaid is invalid"))?; .ok_or(Error::BadDatabase("Media ID in db is invalid."))?;
let filename = if filename_bytes.is_empty() { let filename = if filename_bytes.is_empty() {
None None
} else { } else {
Some(utils::string_from_bytes(filename_bytes)?) Some(
utils::string_from_bytes(filename_bytes)
.map_err(|_| Error::BadDatabase("Filename in db is invalid."))?,
)
}; };
Ok(Some((filename, content_type, file.to_vec()))) Ok(Some((filename, content_type, file.to_vec())))
@ -110,16 +120,21 @@ impl Media {
let content_type = utils::string_from_bytes( let content_type = utils::string_from_bytes(
parts parts
.next() .next()
.ok_or(Error::BadDatabase("mediaid is invalid"))?, .ok_or(Error::BadDatabase("Media ID in db is invalid"))?,
)?; )
.map_err(|_| Error::BadDatabase("Invalid content type in db."))?;
let filename_bytes = parts let filename_bytes = parts
.next() .next()
.ok_or(Error::BadDatabase("mediaid is invalid"))?; .ok_or(Error::BadDatabase("Media ID in db is invalid"))?;
let filename = if filename_bytes.is_empty() { let filename = if filename_bytes.is_empty() {
None None
} else { } else {
Some(utils::string_from_bytes(filename_bytes)?) Some(
utils::string_from_bytes(filename_bytes)
.map_err(|_| Error::BadDatabase("Filename in db is invalid."))?,
)
}; };
if let Ok(image) = image::load_from_memory(&file) { if let Ok(image) = image::load_from_memory(&file) {

View File

@ -5,6 +5,7 @@ pub use edus::RoomEdus;
use crate::{utils, Error, PduEvent, Result}; use crate::{utils, Error, PduEvent, Result};
use log::error; use log::error;
use ruma::{ use ruma::{
api::client::error::ErrorKind,
events::{ events::{
room::{ room::{
join_rules, member, join_rules, member,
@ -61,30 +62,34 @@ impl Rooms {
.roomstateid_pdu .roomstateid_pdu
.scan_prefix(&room_id.to_string().as_bytes()) .scan_prefix(&room_id.to_string().as_bytes())
.values() .values()
.map(|value| Ok::<_, Error>(serde_json::from_slice::<PduEvent>(&value?)?)) .map(|value| {
Ok::<_, Error>(
serde_json::from_slice::<PduEvent>(&value?)
.map_err(|_| Error::BadDatabase("Invalid PDU in db."))?,
)
})
{ {
let pdu = pdu?; let pdu = pdu?;
hashmap.insert( let state_key = pdu.state_key.clone().ok_or(Error::BadDatabase(
( "Room state contains event without state_key.",
pdu.kind.clone(), ))?;
pdu.state_key hashmap.insert((pdu.kind.clone(), state_key), pdu);
.clone()
.expect("state events have a state key"),
),
pdu,
);
} }
Ok(hashmap) Ok(hashmap)
} }
/// Returns the `count` of this pdu's id. /// Returns the `count` of this pdu's id.
pub fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<u64>> { pub fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<u64>> {
Ok(self self.eventid_pduid
.eventid_pduid
.get(event_id.to_string().as_bytes())? .get(event_id.to_string().as_bytes())?
.map(|pdu_id| { .map_or(Ok(None), |pdu_id| {
utils::u64_from_bytes(&pdu_id[pdu_id.len() - mem::size_of::<u64>()..pdu_id.len()]) Ok(Some(
})) utils::u64_from_bytes(
&pdu_id[pdu_id.len() - mem::size_of::<u64>()..pdu_id.len()],
)
.map_err(|_| Error::BadDatabase("PDU has invalid count bytes."))?,
))
})
} }
/// Returns the json of a pdu. /// Returns the json of a pdu.
@ -92,11 +97,12 @@ impl Rooms {
self.eventid_pduid self.eventid_pduid
.get(event_id.to_string().as_bytes())? .get(event_id.to_string().as_bytes())?
.map_or(Ok(None), |pdu_id| { .map_or(Ok(None), |pdu_id| {
Ok(Some(serde_json::from_slice( Ok(Some(
&self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase( serde_json::from_slice(&self.pduid_pdu.get(pdu_id)?.ok_or(
"eventid_pduid points to nonexistent pdu", Error::BadDatabase("eventid_pduid points to nonexistent pdu."),
))?, )?)
)?)) .map_err(|_| Error::BadDatabase("Invalid PDU in db."))?,
))
}) })
} }
@ -112,28 +118,37 @@ impl Rooms {
self.eventid_pduid self.eventid_pduid
.get(event_id.to_string().as_bytes())? .get(event_id.to_string().as_bytes())?
.map_or(Ok(None), |pdu_id| { .map_or(Ok(None), |pdu_id| {
Ok(Some(serde_json::from_slice( Ok(Some(
&self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase( serde_json::from_slice(&self.pduid_pdu.get(pdu_id)?.ok_or(
"eventid_pduid points to nonexistent pdu", Error::BadDatabase("eventid_pduid points to nonexistent pdu."),
))?, )?)
)?)) .map_err(|_| Error::BadDatabase("Invalid PDU in db."))?,
))
}) })
} }
/// Returns the pdu. /// Returns the pdu.
pub fn get_pdu_from_id(&self, pdu_id: &IVec) -> Result<Option<PduEvent>> { pub fn get_pdu_from_id(&self, pdu_id: &IVec) -> Result<Option<PduEvent>> {
self.pduid_pdu self.pduid_pdu.get(pdu_id)?.map_or(Ok(None), |pdu| {
.get(pdu_id)? Ok(Some(
.map_or(Ok(None), |pdu| Ok(Some(serde_json::from_slice(&pdu)?))) serde_json::from_slice(&pdu)
.map_err(|_| Error::BadDatabase("Invalid PDU in db."))?,
))
})
} }
/// Returns the pdu. /// Removes a pdu and creates a new one with the same id.
pub fn replace_pdu(&self, pdu_id: &IVec, pdu: &PduEvent) -> Result<()> { fn replace_pdu(&self, pdu_id: &IVec, pdu: &PduEvent) -> Result<()> {
if self.pduid_pdu.get(&pdu_id)?.is_some() { if self.pduid_pdu.get(&pdu_id)?.is_some() {
self.pduid_pdu self.pduid_pdu.insert(
.insert(&pdu_id, &*serde_json::to_string(pdu)?)?; &pdu_id,
&*serde_json::to_string(pdu).expect("PduEvent::to_string always works"),
)?;
Ok(()) Ok(())
} else { } else {
Err(Error::BadRequest("pdu does not exist")) Err(Error::BadRequest(
ErrorKind::NotFound,
"PDU does not exist.",
))
} }
} }
@ -148,7 +163,12 @@ impl Rooms {
.roomid_pduleaves .roomid_pduleaves
.scan_prefix(prefix) .scan_prefix(prefix)
.values() .values()
.map(|bytes| Ok::<_, Error>(EventId::try_from(&*utils::string_from_bytes(&bytes?)?)?)) .map(|bytes| {
Ok::<_, Error>(
serde_json::from_slice(&bytes?)
.map_err(|_| Error::BadDatabase("Invalid EventID in roomid_pduleaves."))?,
)
})
{ {
events.push(event?); events.push(event?);
} }
@ -214,12 +234,13 @@ impl Rooms {
Ok( Ok(
serde_json::from_value::<EventJson<PowerLevelsEventContent>>( serde_json::from_value::<EventJson<PowerLevelsEventContent>>(
power_levels.content.clone(), power_levels.content.clone(),
)? )
.deserialize()?, .expect("EventJson::from_value always works.")
.deserialize()
.map_err(|_| Error::BadDatabase("Invalid PowerLevels event in db."))?,
) )
}, },
)?; )?;
{
let sender_membership = self let sender_membership = self
.room_state(&room_id)? .room_state(&room_id)?
.get(&(EventType::RoomMember, sender.to_string())) .get(&(EventType::RoomMember, sender.to_string()))
@ -227,8 +248,10 @@ impl Rooms {
Ok( Ok(
serde_json::from_value::<EventJson<member::MemberEventContent>>( serde_json::from_value::<EventJson<member::MemberEventContent>>(
pdu.content.clone(), pdu.content.clone(),
)? )
.deserialize()? .expect("EventJson::from_value always works.")
.deserialize()
.map_err(|_| Error::BadDatabase("Invalid Member event in db."))?
.membership, .membership,
) )
})?; })?;
@ -247,23 +270,34 @@ impl Rooms {
if !match event_type { if !match event_type {
EventType::RoomMember => { EventType::RoomMember => {
let target_user_id = UserId::try_from(&**state_key)?; let target_user_id = UserId::try_from(&**state_key).map_err(|_| {
Error::BadRequest(
ErrorKind::InvalidParam,
"State key of member event does not contain user id.",
)
})?;
let current_membership = self let current_membership = self
.room_state(&room_id)? .room_state(&room_id)?
.get(&(EventType::RoomMember, target_user_id.to_string())) .get(&(EventType::RoomMember, target_user_id.to_string()))
.map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| { .map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| {
Ok(serde_json::from_value::< Ok(
EventJson<member::MemberEventContent>, serde_json::from_value::<EventJson<member::MemberEventContent>>(
>(pdu.content.clone())? pdu.content.clone(),
.deserialize()? )
.membership) .expect("EventJson::from_value always works.")
.deserialize()
.map_err(|_| Error::BadDatabase("Invalid Member event in db."))?
.membership,
)
})?; })?;
let target_membership = serde_json::from_value::< let target_membership = serde_json::from_value::<
EventJson<member::MemberEventContent>, EventJson<member::MemberEventContent>,
>(content.clone())? >(content.clone())
.deserialize()? .expect("EventJson::from_value always works.")
.deserialize()
.map_err(|_| Error::BadDatabase("Invalid Member event in db."))?
.membership; .membership;
let target_power = power_levels.users.get(&target_user_id).map_or_else( let target_power = power_levels.users.get(&target_user_id).map_or_else(
@ -278,26 +312,29 @@ impl Rooms {
Some, Some,
); );
let join_rules = self let join_rules =
.room_state(&room_id)? self.room_state(&room_id)?
.get(&(EventType::RoomJoinRules, "".to_owned())) .get(&(EventType::RoomJoinRules, "".to_owned()))
.map_or(join_rules::JoinRule::Public, |pdu| { .map_or(Ok::<_, Error>(join_rules::JoinRule::Public), |pdu| {
serde_json::from_value::< Ok(serde_json::from_value::<
EventJson<join_rules::JoinRulesEventContent>, EventJson<join_rules::JoinRulesEventContent>,
>(pdu.content.clone()) >(pdu.content.clone())
.unwrap() .expect("EventJson::from_value always works.")
.deserialize() .deserialize()
.unwrap() .map_err(|_| {
.join_rule Error::BadDatabase("Database contains invalid JoinRules event")
}); })?
.join_rule)
})?;
let authorized = if target_membership == member::MembershipState::Join { let authorized = if target_membership == member::MembershipState::Join {
let mut prev_events = prev_events.iter(); let mut prev_events = prev_events.iter();
let prev_event = self let prev_event = self
.get_pdu(prev_events.next().ok_or(Error::BadRequest( .get_pdu(prev_events.next().ok_or(Error::BadRequest(
"membership can't be the first event", ErrorKind::Unknown,
"Membership can't be the first event",
))?)? ))?)?
.ok_or(Error::BadDatabase("pdu leave points to valid event"))?; .ok_or(Error::BadDatabase("PDU leaf points to invalid event!"))?;
if prev_event.kind == EventType::RoomCreate if prev_event.kind == EventType::RoomCreate
&& prev_event.prev_events.is_empty() && prev_event.prev_events.is_empty()
{ {
@ -313,15 +350,20 @@ impl Rooms {
|| join_rules == join_rules::JoinRule::Public || join_rules == join_rules::JoinRule::Public
} }
} else if target_membership == member::MembershipState::Invite { } else if target_membership == member::MembershipState::Invite {
if let Some(third_party_invite_json) = content.get("third_party_invite") if let Some(third_party_invite_json) = content.get("third_party_invite") {
{
if current_membership == member::MembershipState::Ban { if current_membership == member::MembershipState::Ban {
false false
} else { } else {
let _third_party_invite = let _third_party_invite =
serde_json::from_value::<member::ThirdPartyInvite>( serde_json::from_value::<member::ThirdPartyInvite>(
third_party_invite_json.clone(), third_party_invite_json.clone(),
)?; )
.map_err(|_| {
Error::BadRequest(
ErrorKind::InvalidParam,
"ThirdPartyInvite is invalid",
)
})?;
todo!("handle third party invites"); todo!("handle third party invites");
} }
} else if sender_membership != member::MembershipState::Join } else if sender_membership != member::MembershipState::Join
@ -377,11 +419,18 @@ impl Rooms {
} { } {
error!("Unauthorized"); error!("Unauthorized");
// Not authorized // Not authorized
return Err(Error::BadRequest("event not authorized")); return Err(Error::BadRequest(
} ErrorKind::Forbidden,
"Event is not authorized",
));
} }
} else if !self.is_joined(&sender, &room_id)? { } else if !self.is_joined(&sender, &room_id)? {
return Err(Error::BadRequest("event not authorized")); // TODO: auth rules apply to all events, not only those with a state key
error!("Unauthorized");
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"Event is not authorized",
));
} }
// Our depth is the maximum depth of prev_events + 1 // Our depth is the maximum depth of prev_events + 1
@ -410,14 +459,14 @@ impl Rooms {
origin: globals.server_name().to_owned(), origin: globals.server_name().to_owned(),
origin_server_ts: utils::millis_since_unix_epoch() origin_server_ts: utils::millis_since_unix_epoch()
.try_into() .try_into()
.expect("this only fails many years in the future"), .expect("time is valid"),
kind: event_type.clone(), kind: event_type.clone(),
content: content.clone(), content: content.clone(),
state_key, state_key,
prev_events, prev_events,
depth: depth depth: depth
.try_into() .try_into()
.expect("depth can overflow and should be deprecated..."), .map_err(|_| Error::BadDatabase("Depth is invalid"))?,
auth_events: Vec::new(), auth_events: Vec::new(),
redacts: redacts.clone(), redacts: redacts.clone(),
unsigned, unsigned,
@ -430,18 +479,20 @@ impl Rooms {
// Generate event id // Generate event id
pdu.event_id = EventId::try_from(&*format!( pdu.event_id = EventId::try_from(&*format!(
"${}", "${}",
ruma::signatures::reference_hash(&serde_json::to_value(&pdu)?) ruma::signatures::reference_hash(
&serde_json::to_value(&pdu).expect("event is valid, we just created it")
)
.expect("ruma can calculate reference hashes") .expect("ruma can calculate reference hashes")
)) ))
.expect("ruma's reference hashes are correct"); .expect("ruma's reference hashes are valid event ids");
let mut pdu_json = serde_json::to_value(&pdu)?; let mut pdu_json = serde_json::to_value(&pdu).expect("event is valid, we just created it");
ruma::signatures::hash_and_sign_event( ruma::signatures::hash_and_sign_event(
globals.server_name(), globals.server_name(),
globals.keypair(), globals.keypair(),
&mut pdu_json, &mut pdu_json,
) )
.expect("our new event can be hashed and signed"); .expect("event is valid, we just created it");
self.replace_pdu_leaves(&room_id, &pdu.event_id)?; self.replace_pdu_leaves(&room_id, &pdu.event_id)?;
@ -473,8 +524,15 @@ impl Rooms {
// TODO: Reason // TODO: Reason
let _reason = serde_json::from_value::< let _reason = serde_json::from_value::<
EventJson<redaction::RedactionEventContent>, EventJson<redaction::RedactionEventContent>,
>(content)? >(content)
.deserialize()? .expect("EventJson::from_value always works.")
.deserialize()
.map_err(|_| {
Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid redaction event content.",
)
})?
.reason; .reason;
self.redact_pdu(&redact_id)?; self.redact_pdu(&redact_id)?;
@ -528,7 +586,10 @@ impl Rooms {
}) })
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix)) .take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))) .map(|(_, v)| {
Ok(serde_json::from_slice(&v)
.map_err(|_| Error::BadDatabase("PDU in db is invalid."))?)
}))
} }
/// Returns an iterator over all events in a room that happened before the event with id /// Returns an iterator over all events in a room that happened before the event with id
@ -552,7 +613,10 @@ impl Rooms {
.rev() .rev()
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix)) .take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| Ok(serde_json::from_slice(&v)?)) .map(|(_, v)| {
Ok(serde_json::from_slice(&v)
.map_err(|_| Error::BadDatabase("PDU in db is invalid."))?)
})
} }
/// Returns an iterator over all events in a room that happened after the event with id /// Returns an iterator over all events in a room that happened after the event with id
@ -575,7 +639,10 @@ impl Rooms {
.range(current..) .range(current..)
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix)) .take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| Ok(serde_json::from_slice(&v)?)) .map(|(_, v)| {
Ok(serde_json::from_slice(&v)
.map_err(|_| Error::BadDatabase("PDU in db is invalid."))?)
})
} }
/// Replace a PDU with the redacted form. /// Replace a PDU with the redacted form.
@ -583,12 +650,15 @@ impl Rooms {
if let Some(pdu_id) = self.get_pdu_id(event_id)? { if let Some(pdu_id) = self.get_pdu_id(event_id)? {
let mut pdu = self let mut pdu = self
.get_pdu_from_id(&pdu_id)? .get_pdu_from_id(&pdu_id)?
.ok_or(Error::BadDatabase("pduid points to invalid pdu"))?; .ok_or(Error::BadDatabase("PDU ID points to invalid PDU."))?;
pdu.redact(); pdu.redact()?;
self.replace_pdu(&pdu_id, &pdu)?; self.replace_pdu(&pdu_id, &pdu)?;
Ok(()) Ok(())
} else { } else {
Err(Error::BadRequest("eventid does not exist")) Err(Error::BadRequest(
ErrorKind::NotFound,
"Event ID does not exist.",
))
} }
} }
@ -664,7 +734,10 @@ impl Rooms {
let room_id = self let room_id = self
.alias_roomid .alias_roomid
.remove(alias.alias())? .remove(alias.alias())?
.ok_or(Error::BadRequest("Alias does not exist"))?; .ok_or(Error::BadRequest(
ErrorKind::NotFound,
"Alias does not exist.",
))?;
for key in self.aliasid_alias.scan_prefix(room_id).keys() { for key in self.aliasid_alias.scan_prefix(room_id).keys() {
self.aliasid_alias.remove(key?)?; self.aliasid_alias.remove(key?)?;
@ -678,7 +751,9 @@ impl Rooms {
self.alias_roomid self.alias_roomid
.get(alias.alias())? .get(alias.alias())?
.map_or(Ok(None), |bytes| { .map_or(Ok(None), |bytes| {
Ok(Some(RoomId::try_from(utils::string_from_bytes(&bytes)?)?)) Ok(Some(serde_json::from_slice(&bytes).map_err(|_| {
Error::BadDatabase("Room ID in alias_roomid is invalid.")
})?))
}) })
} }
@ -689,7 +764,10 @@ impl Rooms {
self.aliasid_alias self.aliasid_alias
.scan_prefix(prefix) .scan_prefix(prefix)
.values() .values()
.map(|bytes| Ok(RoomAliasId::try_from(utils::string_from_bytes(&bytes?)?)?)) .map(|bytes| {
Ok(serde_json::from_slice(&bytes?)
.map_err(|_| Error::BadDatabase("Alias in aliasid_alias is invalid."))?)
})
} }
pub fn set_public(&self, room_id: &RoomId, public: bool) -> Result<()> { pub fn set_public(&self, room_id: &RoomId, public: bool) -> Result<()> {
@ -707,10 +785,10 @@ impl Rooms {
} }
pub fn public_rooms(&self) -> impl Iterator<Item = Result<RoomId>> { pub fn public_rooms(&self) -> impl Iterator<Item = Result<RoomId>> {
self.publicroomids self.publicroomids.iter().keys().map(|bytes| {
.iter() Ok(serde_json::from_slice(&bytes?)
.keys() .map_err(|_| Error::BadDatabase("Room ID in publicroomids is invalid."))?)
.map(|bytes| Ok(RoomId::try_from(utils::string_from_bytes(&bytes?)?)?)) })
} }
/// Returns an iterator over all rooms a user joined. /// Returns an iterator over all rooms a user joined.
@ -719,12 +797,13 @@ impl Rooms {
.scan_prefix(room_id.to_string()) .scan_prefix(room_id.to_string())
.values() .values()
.map(|key| { .map(|key| {
Ok(UserId::try_from(&*utils::string_from_bytes( Ok(serde_json::from_slice(
&key? &key?
.rsplit(|&b| b == 0xff) .rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?, .ok_or(Error::BadDatabase("RoomUser ID is invalid."))?,
)?)?) )
.map_err(|_| Error::BadDatabase("Invalid User ID in db."))?)
}) })
} }
@ -734,12 +813,13 @@ impl Rooms {
.scan_prefix(room_id.to_string()) .scan_prefix(room_id.to_string())
.keys() .keys()
.map(|key| { .map(|key| {
Ok(UserId::try_from(&*utils::string_from_bytes( Ok(serde_json::from_slice(
&key? &key?
.rsplit(|&b| b == 0xff) .rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?, .ok_or(Error::BadDatabase("RoomUser ID is invalid."))?,
)?)?) )
.map_err(|_| Error::BadDatabase("Invalid User ID in db."))?)
}) })
} }
@ -749,12 +829,13 @@ impl Rooms {
.scan_prefix(user_id.to_string()) .scan_prefix(user_id.to_string())
.keys() .keys()
.map(|key| { .map(|key| {
Ok(RoomId::try_from(&*utils::string_from_bytes( Ok(serde_json::from_slice(
&key? &key?
.rsplit(|&b| b == 0xff) .rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?, .ok_or(Error::BadDatabase("UserRoom ID is invalid."))?,
)?)?) )
.map_err(|_| Error::BadDatabase("Invalid Room ID in db."))?)
}) })
} }
@ -764,12 +845,13 @@ impl Rooms {
.scan_prefix(&user_id.to_string()) .scan_prefix(&user_id.to_string())
.keys() .keys()
.map(|key| { .map(|key| {
Ok(RoomId::try_from(&*utils::string_from_bytes( Ok(serde_json::from_slice(
&key? &key?
.rsplit(|&b| b == 0xff) .rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?, .ok_or(Error::BadDatabase("UserRoom ID is invalid."))?,
)?)?) )
.map_err(|_| Error::BadDatabase("Invalid Room ID in db."))?)
}) })
} }
@ -779,12 +861,13 @@ impl Rooms {
.scan_prefix(&user_id.to_string()) .scan_prefix(&user_id.to_string())
.keys() .keys()
.map(|key| { .map(|key| {
Ok(RoomId::try_from(&*utils::string_from_bytes( Ok(serde_json::from_slice(
&key? &key?
.rsplit(|&b| b == 0xff) .rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?, .ok_or(Error::BadDatabase("UserRoom ID is invalid."))?,
)?)?) )
.map_err(|_| Error::BadDatabase("Invalid Room ID in db."))?)
}) })
} }

View File

@ -33,7 +33,10 @@ impl RoomEdus {
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.take_while(|key| key.starts_with(&prefix)) .take_while(|key| key.starts_with(&prefix))
.find(|key| { .find(|key| {
key.rsplit(|&b| b == 0xff).next().unwrap() == user_id.to_string().as_bytes() key.rsplit(|&b| b == 0xff)
.next()
.expect("rsplit always returns an element")
== user_id.to_string().as_bytes()
}) })
{ {
// This is the old room_latest // This is the old room_latest
@ -45,8 +48,10 @@ impl RoomEdus {
room_latest_id.push(0xff); room_latest_id.push(0xff);
room_latest_id.extend_from_slice(&user_id.to_string().as_bytes()); room_latest_id.extend_from_slice(&user_id.to_string().as_bytes());
self.roomlatestid_roomlatest self.roomlatestid_roomlatest.insert(
.insert(room_latest_id, &*serde_json::to_string(&event)?)?; room_latest_id,
&*serde_json::to_string(&event).expect("EduEvent::to_string always works"),
)?;
Ok(()) Ok(())
} }
@ -68,7 +73,10 @@ impl RoomEdus {
.range(&*first_possible_edu..) .range(&*first_possible_edu..)
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix)) .take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))) .map(|(_, v)| {
Ok(serde_json::from_slice(&v)
.map_err(|_| Error::BadDatabase("Read receipt in db is invalid."))?)
}))
} }
/// Sets a user as typing until the timeout timestamp is reached or roomactive_remove is /// Sets a user as typing until the timeout timestamp is reached or roomactive_remove is
@ -152,17 +160,21 @@ impl RoomEdus {
.roomactiveid_userid .roomactiveid_userid
.scan_prefix(&prefix) .scan_prefix(&prefix)
.keys() .keys()
.filter_map(|r| r.ok()) .map(|key| {
.take_while(|k| { let key = key?;
utils::u64_from_bytes( Ok::<_, Error>((
k.split(|&c| c == 0xff) key.clone(),
.nth(1) utils::u64_from_bytes(key.split(|&b| b == 0xff).nth(1).ok_or(
.expect("roomactive has valid timestamp and delimiters"), Error::BadDatabase("RoomActive has invalid timestamp or delimiters."),
) < current_timestamp )?)
.map_err(|_| Error::BadDatabase("RoomActive has invalid timestamp bytes."))?,
))
}) })
.filter_map(|r| r.ok())
.take_while(|&(_, timestamp)| timestamp < current_timestamp)
{ {
// This is an outdated edu (time > timestamp) // This is an outdated edu (time > timestamp)
self.roomactiveid_userid.remove(outdated_edu)?; self.roomactiveid_userid.remove(outdated_edu.0)?;
found_outdated = true; found_outdated = true;
} }
@ -187,7 +199,11 @@ impl RoomEdus {
Ok(self Ok(self
.roomid_lastroomactiveupdate .roomid_lastroomactiveupdate
.get(&room_id.to_string().as_bytes())? .get(&room_id.to_string().as_bytes())?
.map(|bytes| utils::u64_from_bytes(&bytes)) .map_or(Ok::<_, Error>(None), |bytes| {
Ok(Some(
utils::u64_from_bytes(&bytes).map_err(|_| Error::BadDatabase(""))?,
))
})?
.unwrap_or(0)) .unwrap_or(0))
} }
@ -202,7 +218,11 @@ impl RoomEdus {
.roomactiveid_userid .roomactiveid_userid
.scan_prefix(prefix) .scan_prefix(prefix)
.values() .values()
.map(|user_id| Ok::<_, Error>(UserId::try_from(utils::string_from_bytes(&user_id?)?)?)) .map(|user_id| {
Ok::<_, Error>(serde_json::from_slice(&user_id?).map_err(|_| {
Error::BadDatabase("User ID in roomactiveid_userid is invalid.")
})?)
})
{ {
user_ids.push(user_id?); user_ids.push(user_id?);
} }
@ -230,9 +250,10 @@ impl RoomEdus {
key.push(0xff); key.push(0xff);
key.extend_from_slice(&user_id.to_string().as_bytes()); key.extend_from_slice(&user_id.to_string().as_bytes());
Ok(self self.roomuserid_lastread.get(key)?.map_or(Ok(None), |v| {
.roomuserid_lastread Ok(Some(utils::u64_from_bytes(&v).map_err(|_| {
.get(key)? Error::BadDatabase("Invalid private read marker bytes")
.map(|v| utils::u64_from_bytes(&v))) })?))
})
} }
} }

View File

@ -43,15 +43,51 @@ impl Uiaa {
// Find out what the user completed // Find out what the user completed
match &**kind { match &**kind {
"m.login.password" => { "m.login.password" => {
if auth_parameters["identifier"]["type"] != "m.id.user" { let identifier = auth_parameters.get("identifier").ok_or(Error::BadRequest(
panic!("identifier not supported"); ErrorKind::MissingParam,
"m.login.password needs identifier.",
))?;
let identifier_type = identifier.get("type").ok_or(Error::BadRequest(
ErrorKind::MissingParam,
"Identifier needs a type.",
))?;
if identifier_type != "m.id.user" {
return Err(Error::BadRequest(
ErrorKind::Unrecognized,
"Identifier type not recognized.",
));
} }
let user_id = UserId::parse_with_server_name( let username = identifier
auth_parameters["identifier"]["user"].as_str().unwrap(), .get("user")
globals.server_name(), .ok_or(Error::BadRequest(
)?; ErrorKind::MissingParam,
let password = auth_parameters["password"].as_str().unwrap(); "Identifier needs user field.",
))?
.as_str()
.ok_or(Error::BadRequest(
ErrorKind::BadJson,
"User is not a string.",
))?;
let user_id = UserId::parse_with_server_name(username, globals.server_name())
.map_err(|_| {
Error::BadRequest(ErrorKind::InvalidParam, "User ID is invalid.")
})?;
let password = auth_parameters
.get("password")
.ok_or(Error::BadRequest(
ErrorKind::MissingParam,
"Password is missing.",
))?
.as_str()
.ok_or(Error::BadRequest(
ErrorKind::BadJson,
"Password is not a string.",
))?;
// Check if password is correct // Check if password is correct
if let Some(hash) = users.password_hash(&user_id)? { if let Some(hash) = users.password_hash(&user_id)? {
@ -59,7 +95,6 @@ impl Uiaa {
argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false); argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false);
if !hash_matches { if !hash_matches {
debug!("Invalid password.");
uiaainfo.auth_error = Some(ruma::api::client::error::ErrorBody { uiaainfo.auth_error = Some(ruma::api::client::error::ErrorBody {
kind: ErrorKind::Forbidden, kind: ErrorKind::Forbidden,
message: "Invalid username or password.".to_owned(), message: "Invalid username or password.".to_owned(),
@ -113,8 +148,10 @@ impl Uiaa {
userdeviceid.extend_from_slice(device_id.as_bytes()); userdeviceid.extend_from_slice(device_id.as_bytes());
if let Some(uiaainfo) = uiaainfo { if let Some(uiaainfo) = uiaainfo {
self.userdeviceid_uiaainfo self.userdeviceid_uiaainfo.insert(
.insert(&userdeviceid, &*serde_json::to_string(&uiaainfo)?)?; &userdeviceid,
&*serde_json::to_string(&uiaainfo).expect("UiaaInfo::to_string always works"),
)?;
} else { } else {
self.userdeviceid_uiaainfo.remove(&userdeviceid)?; self.userdeviceid_uiaainfo.remove(&userdeviceid)?;
} }
@ -136,8 +173,12 @@ impl Uiaa {
&self &self
.userdeviceid_uiaainfo .userdeviceid_uiaainfo
.get(&userdeviceid)? .get(&userdeviceid)?
.ok_or(Error::BadRequest("session does not exist"))?, .ok_or(Error::BadRequest(
)?; ErrorKind::Forbidden,
"UIAA session does not exist.",
))?,
)
.map_err(|_| Error::BadDatabase("UiaaInfo in userdeviceid_uiaainfo is invalid."))?;
if uiaainfo if uiaainfo
.session .session
@ -145,7 +186,10 @@ impl Uiaa {
.filter(|&s| s == session) .filter(|&s| s == session)
.is_none() .is_none()
{ {
return Err(Error::BadRequest("wrong session token")); return Err(Error::BadRequest(
ErrorKind::Forbidden,
"UIAA session token invalid.",
));
} }
Ok(uiaainfo) Ok(uiaainfo)

View File

@ -43,24 +43,29 @@ impl Users {
.get(token)? .get(token)?
.map_or(Ok(None), |bytes| { .map_or(Ok(None), |bytes| {
let mut parts = bytes.split(|&b| b == 0xff); let mut parts = bytes.split(|&b| b == 0xff);
let user_bytes = parts let user_bytes = parts.next().ok_or(Error::BadDatabase(
.next() "token_userdeviceid value in db is invalid.",
.ok_or(Error::BadDatabase("token_userdeviceid value invalid"))?; ))?;
let device_bytes = parts let device_bytes = parts.next().ok_or(Error::BadDatabase(
.next() "token_userdeviceid value in db is invalid.",
.ok_or(Error::BadDatabase("token_userdeviceid value invalid"))?; ))?;
Ok(Some(( Ok(Some((
UserId::try_from(utils::string_from_bytes(&user_bytes)?)?, serde_json::from_slice(&user_bytes).map_err(|_| {
utils::string_from_bytes(&device_bytes)?, Error::BadDatabase("User ID in token_userdeviceid is invalid.")
})?,
utils::string_from_bytes(&device_bytes).map_err(|_| {
Error::BadDatabase("Device ID in token_userdeviceid is invalid.")
})?,
))) )))
}) })
} }
/// Returns an iterator over all users on this homeserver. /// Returns an iterator over all users on this homeserver.
pub fn iter(&self) -> impl Iterator<Item = Result<UserId>> { pub fn iter(&self) -> impl Iterator<Item = Result<UserId>> {
self.userid_password.iter().keys().map(|r| { self.userid_password.iter().keys().map(|bytes| {
utils::string_from_bytes(&r?).and_then(|string| Ok(UserId::try_from(&*string)?)) Ok(serde_json::from_slice(&bytes?)
.map_err(|_| Error::BadDatabase("User ID bytes in db are invalid."))?)
}) })
} }
@ -68,14 +73,22 @@ impl Users {
pub fn password_hash(&self, user_id: &UserId) -> Result<Option<String>> { pub fn password_hash(&self, user_id: &UserId) -> Result<Option<String>> {
self.userid_password self.userid_password
.get(user_id.to_string())? .get(user_id.to_string())?
.map_or(Ok(None), |bytes| utils::string_from_bytes(&bytes).map(Some)) .map_or(Ok(None), |bytes| {
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
Error::BadDatabase("Password hash in db is not valid string.")
})?))
})
} }
/// Returns the displayname of a user on this homeserver. /// Returns the displayname of a user on this homeserver.
pub fn displayname(&self, user_id: &UserId) -> Result<Option<String>> { pub fn displayname(&self, user_id: &UserId) -> Result<Option<String>> {
self.userid_displayname self.userid_displayname
.get(user_id.to_string())? .get(user_id.to_string())?
.map_or(Ok(None), |bytes| utils::string_from_bytes(&bytes).map(Some)) .map_or(Ok(None), |bytes| {
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
Error::BadDatabase("Displayname in db is invalid.")
})?))
})
} }
/// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change. /// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change.
@ -94,7 +107,11 @@ impl Users {
pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<String>> { pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<String>> {
self.userid_avatarurl self.userid_avatarurl
.get(user_id.to_string())? .get(user_id.to_string())?
.map_or(Ok(None), |bytes| utils::string_from_bytes(&bytes).map(Some)) .map_or(Ok(None), |bytes| {
Ok(Some(utils::string_from_bytes(&bytes).map_err(|_| {
Error::BadDatabase("Avatar URL in db is invalid.")
})?))
})
} }
/// Sets a new avatar_url or removes it if avatar_url is None. /// Sets a new avatar_url or removes it if avatar_url is None.
@ -117,11 +134,8 @@ impl Users {
token: &str, token: &str,
initial_device_display_name: Option<String>, initial_device_display_name: Option<String>,
) -> Result<()> { ) -> Result<()> {
if !self.exists(user_id)? { // This method should never be called for nonexistent users.
return Err(Error::BadRequest( assert!(self.exists(user_id)?);
"tried to create device for nonexistent user",
));
}
let mut userdeviceid = user_id.to_string().as_bytes().to_vec(); let mut userdeviceid = user_id.to_string().as_bytes().to_vec();
userdeviceid.push(0xff); userdeviceid.push(0xff);
@ -134,7 +148,8 @@ impl Users {
display_name: initial_device_display_name, display_name: initial_device_display_name,
last_seen_ip: None, // TODO last_seen_ip: None, // TODO
last_seen_ts: Some(SystemTime::now()), last_seen_ts: Some(SystemTime::now()),
})? })
.expect("Device::to_string never fails.")
.as_bytes(), .as_bytes(),
)?; )?;
@ -185,23 +200,22 @@ impl Users {
&*bytes? &*bytes?
.rsplit(|&b| b == 0xff) .rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("userdeviceid is invalid"))?, .ok_or(Error::BadDatabase("UserDevice ID in db is invalid."))?,
)?) )
.map_err(|_| {
Error::BadDatabase("Device ID in userdeviceid_metadata is invalid.")
})?)
}) })
} }
/// Replaces the access token of one device. /// Replaces the access token of one device.
pub fn set_token(&self, user_id: &UserId, device_id: &str, token: &str) -> Result<()> { fn set_token(&self, user_id: &UserId, device_id: &str, token: &str) -> Result<()> {
let mut userdeviceid = user_id.to_string().as_bytes().to_vec(); let mut userdeviceid = user_id.to_string().as_bytes().to_vec();
userdeviceid.push(0xff); userdeviceid.push(0xff);
userdeviceid.extend_from_slice(device_id.as_bytes()); userdeviceid.extend_from_slice(device_id.as_bytes());
// All devices have metadata // All devices have metadata
if self.userdeviceid_metadata.get(&userdeviceid)?.is_none() { assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some());
return Err(Error::BadRequest(
"Tried to set token for nonexistent device",
));
}
// Remove old token // Remove old token
if let Some(old_token) = self.userdeviceid_token.get(&userdeviceid)? { if let Some(old_token) = self.userdeviceid_token.get(&userdeviceid)? {
@ -228,19 +242,23 @@ impl Users {
key.extend_from_slice(device_id.as_bytes()); key.extend_from_slice(device_id.as_bytes());
// All devices have metadata // All devices have metadata
if self.userdeviceid_metadata.get(&key)?.is_none() { // Only existing devices should be able to call this.
return Err(Error::BadRequest( assert!(self.userdeviceid_metadata.get(&key)?.is_some());
"Tried to set token for nonexistent device",
));
}
key.push(0xff); key.push(0xff);
// TODO: Use AlgorithmAndDeviceId::to_string when it's available (and update everything, // TODO: Use AlgorithmAndDeviceId::to_string when it's available (and update everything,
// because there are no wrapping quotation marks anymore) // because there are no wrapping quotation marks anymore)
key.extend_from_slice(&serde_json::to_string(one_time_key_key)?.as_bytes()); key.extend_from_slice(
&serde_json::to_string(one_time_key_key)
.expect("AlgorithmAndDeviceId::to_string always works")
.as_bytes(),
);
self.onetimekeyid_onetimekeys self.onetimekeyid_onetimekeys.insert(
.insert(&key, &*serde_json::to_string(&one_time_key_value)?)?; &key,
&*serde_json::to_string(&one_time_key_value)
.expect("OneTimeKey::to_string always works"),
)?;
Ok(()) Ok(())
} }
@ -271,9 +289,11 @@ impl Users {
&*key &*key
.rsplit(|&b| b == 0xff) .rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("onetimekeyid is invalid"))?, .ok_or(Error::BadDatabase("OneTimeKeyId in db is invalid."))?,
)?, )
serde_json::from_slice(&*value)?, .map_err(|_| Error::BadDatabase("OneTimeKeyId in db is invalid."))?,
serde_json::from_slice(&*value)
.map_err(|_| Error::BadDatabase("OneTimeKeys in db are invalid."))?,
)) ))
}) })
.transpose() .transpose()
@ -300,8 +320,9 @@ impl Users {
&*bytes? &*bytes?
.rsplit(|&b| b == 0xff) .rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("onetimekeyid is invalid"))?, .ok_or(Error::BadDatabase("OneTimeKey ID in db is invalid."))?,
)? )
.map_err(|_| Error::BadDatabase("AlgorithmAndDeviceID in db is invalid."))?
.0, .0,
) )
}) })
@ -323,8 +344,10 @@ impl Users {
userdeviceid.push(0xff); userdeviceid.push(0xff);
userdeviceid.extend_from_slice(device_id.as_bytes()); userdeviceid.extend_from_slice(device_id.as_bytes());
self.userdeviceid_devicekeys self.userdeviceid_devicekeys.insert(
.insert(&userdeviceid, &*serde_json::to_string(&device_keys)?)?; &userdeviceid,
&*serde_json::to_string(&device_keys).expect("DeviceKeys::to_string always works"),
)?;
self.devicekeychangeid_userid self.devicekeychangeid_userid
.insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?; .insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?;
@ -344,14 +367,21 @@ impl Users {
self.userdeviceid_devicekeys self.userdeviceid_devicekeys
.scan_prefix(key) .scan_prefix(key)
.values() .values()
.map(|bytes| Ok(serde_json::from_slice(&bytes?)?)) .map(|bytes| {
Ok(serde_json::from_slice(&bytes?)
.map_err(|_| Error::BadDatabase("DeviceKeys in db are invalid."))?)
})
} }
pub fn device_keys_changed(&self, since: u64) -> impl Iterator<Item = Result<UserId>> { pub fn device_keys_changed(&self, since: u64) -> impl Iterator<Item = Result<UserId>> {
self.devicekeychangeid_userid self.devicekeychangeid_userid
.range(since.to_be_bytes()..) .range(since.to_be_bytes()..)
.values() .values()
.map(|bytes| Ok(UserId::try_from(utils::string_from_bytes(&bytes?)?)?)) .map(|bytes| {
Ok(serde_json::from_slice(&bytes?).map_err(|_| {
Error::BadDatabase("User ID in devicekeychangeid_userid is invalid.")
})?)
})
} }
pub fn all_device_keys( pub fn all_device_keys(
@ -366,9 +396,14 @@ impl Users {
let userdeviceid = utils::string_from_bytes( let userdeviceid = utils::string_from_bytes(
key.rsplit(|&b| b == 0xff) key.rsplit(|&b| b == 0xff)
.next() .next()
.ok_or(Error::BadDatabase("userdeviceid is invalid"))?, .ok_or(Error::BadDatabase("UserDeviceID in db is invalid."))?,
)?; )
Ok((userdeviceid, serde_json::from_slice(&*value)?)) .map_err(|_| Error::BadDatabase("UserDeviceId in db is invalid."))?;
Ok((
userdeviceid,
serde_json::from_slice(&*value)
.map_err(|_| Error::BadDatabase("DeviceKeys in db are invalid."))?,
))
}) })
} }
@ -392,8 +427,10 @@ impl Users {
json.insert("sender".to_owned(), sender.to_string().into()); json.insert("sender".to_owned(), sender.to_string().into());
json.insert("content".to_owned(), content); json.insert("content".to_owned(), content);
self.todeviceid_events self.todeviceid_events.insert(
.insert(&key, &*serde_json::to_string(&json)?)?; &key,
&*serde_json::to_string(&json).expect("Map::to_string always works"),
)?;
Ok(()) Ok(())
} }
@ -413,7 +450,10 @@ impl Users {
for result in self.todeviceid_events.scan_prefix(&prefix).take(max) { for result in self.todeviceid_events.scan_prefix(&prefix).take(max) {
let (key, value) = result?; let (key, value) = result?;
events.push(serde_json::from_slice(&*value)?); events.push(
serde_json::from_slice(&*value)
.map_err(|_| Error::BadDatabase("Event in todeviceid_events is invalid."))?,
);
self.todeviceid_events.remove(key)?; self.todeviceid_events.remove(key)?;
} }
@ -430,12 +470,15 @@ impl Users {
userdeviceid.push(0xff); userdeviceid.push(0xff);
userdeviceid.extend_from_slice(device_id.as_bytes()); userdeviceid.extend_from_slice(device_id.as_bytes());
if self.userdeviceid_metadata.get(&userdeviceid)?.is_none() { // Only existing devices should be able to call this.
return Err(Error::BadRequest("device does not exist")); assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some());
}
self.userdeviceid_metadata self.userdeviceid_metadata.insert(
.insert(userdeviceid, serde_json::to_string(device)?.as_bytes())?; userdeviceid,
serde_json::to_string(device)
.expect("Device::to_string always works")
.as_bytes(),
)?;
Ok(()) Ok(())
} }
@ -448,7 +491,11 @@ impl Users {
self.userdeviceid_metadata self.userdeviceid_metadata
.get(&userdeviceid)? .get(&userdeviceid)?
.map_or(Ok(None), |bytes| Ok(Some(serde_json::from_slice(&bytes)?))) .map_or(Ok(None), |bytes| {
Ok(Some(serde_json::from_slice(&bytes).map_err(|_| {
Error::BadDatabase("Metadata in userdeviceid_metadata is invalid.")
})?))
})
} }
pub fn all_devices_metadata(&self, user_id: &UserId) -> impl Iterator<Item = Result<Device>> { pub fn all_devices_metadata(&self, user_id: &UserId) -> impl Iterator<Item = Result<Device>> {
@ -458,6 +505,10 @@ impl Users {
self.userdeviceid_metadata self.userdeviceid_metadata
.scan_prefix(key) .scan_prefix(key)
.values() .values()
.map(|bytes| Ok(serde_json::from_slice::<Device>(&bytes?)?)) .map(|bytes| {
Ok(serde_json::from_slice::<Device>(&bytes?).map_err(|_| {
Error::BadDatabase("Device in userdeviceid_metadata is invalid.")
})?)
})
} }
} }

View File

@ -1,41 +1,79 @@
use crate::RumaResponse;
use http::StatusCode;
use rocket::{
response::{self, Responder},
Request,
};
use ruma::api::client::{
error::{Error as RumaError, ErrorKind},
r0::uiaa::{UiaaInfo, UiaaResponse},
};
use thiserror::Error; use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum Error { pub enum Error {
#[error("problem with the database")] #[error("There was a problem with the connection to the database.")]
SledError { SledError {
#[from] #[from]
source: sled::Error, source: sled::Error,
}, },
#[error("tried to parse invalid string")] #[error("Could not generate an image.")]
StringFromBytesError {
#[from]
source: std::string::FromUtf8Error,
},
#[error("tried to parse invalid identifier")]
SerdeJsonError {
#[from]
source: serde_json::Error,
},
#[error("tried to parse invalid identifier")]
RumaIdentifierError {
#[from]
source: ruma::identifiers::Error,
},
#[error("tried to parse invalid event")]
RumaEventError {
#[from]
source: ruma::events::InvalidEvent,
},
#[error("could not generate image")]
ImageError { ImageError {
#[from] #[from]
source: image::error::ImageError, source: image::error::ImageError,
}, },
#[error("bad request")] #[error("{0}")]
BadRequest(&'static str), BadConfig(&'static str),
#[error("problem in that database")] #[error("{0}")]
BadDatabase(&'static str), BadDatabase(&'static str),
#[error("uiaa")]
Uiaa(UiaaInfo),
#[error("{0}: {1}")]
BadRequest(ErrorKind, &'static str),
#[error("{0}")]
Conflict(&'static str), // This is only needed for when a room alias already exists
}
#[rocket::async_trait]
impl<'r> Responder<'r> for Error {
async fn respond_to(self, r: &'r Request<'_>) -> response::Result<'r> {
if let Self::Uiaa(uiaainfo) = &self {
return RumaResponse::from(UiaaResponse::AuthResponse(uiaainfo.clone()))
.respond_to(r)
.await;
}
let message = format!("{}", self);
use ErrorKind::*;
let (kind, status_code) = match self {
Self::BadRequest(kind, _) => (
kind,
match kind {
Forbidden | GuestAccessForbidden | ThreepidAuthFailed | ThreepidDenied => {
StatusCode::FORBIDDEN
}
Unauthorized | UnknownToken | MissingToken => StatusCode::UNAUTHORIZED,
NotFound => StatusCode::NOT_FOUND,
LimitExceeded => StatusCode::TOO_MANY_REQUESTS,
UserDeactivated => StatusCode::FORBIDDEN,
TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
_ => StatusCode::BAD_REQUEST,
},
),
Self::Conflict(_) => (Unknown, StatusCode::CONFLICT),
_ => (Unknown, StatusCode::INTERNAL_SERVER_ERROR),
};
RumaResponse::from(RumaError {
kind,
message,
status_code,
})
.respond_to(r)
.await
}
} }

View File

@ -12,7 +12,7 @@ mod utils;
pub use database::Database; pub use database::Database;
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use pdu::PduEvent; pub use pdu::PduEvent;
pub use ruma_wrapper::{MatrixResult, Ruma}; pub use ruma_wrapper::{ConduitResult, Ruma, RumaResponse};
use rocket::{fairing::AdHoc, routes}; use rocket::{fairing::AdHoc, routes};
@ -95,7 +95,7 @@ fn setup_rocket() -> rocket::Rocket {
], ],
) )
.attach(AdHoc::on_attach("Config", |rocket| { .attach(AdHoc::on_attach("Config", |rocket| {
let data = Database::load_or_create(&rocket.config()); let data = Database::load_or_create(&rocket.config()).expect("valid config");
Ok(rocket.manage(data)) Ok(rocket.manage(data))
})) }))

View File

@ -1,3 +1,4 @@
use crate::{Error, Result};
use js_int::UInt; use js_int::UInt;
use ruma::{ use ruma::{
api::federation::pdu::EventHash, api::federation::pdu::EventHash,
@ -36,7 +37,7 @@ pub struct PduEvent {
} }
impl PduEvent { impl PduEvent {
pub fn redact(&mut self) { pub fn redact(&mut self) -> Result<()> {
self.unsigned.clear(); self.unsigned.clear();
let allowed = match self.kind { let allowed = match self.kind {
EventType::RoomMember => vec!["membership"], EventType::RoomMember => vec!["membership"],
@ -56,7 +57,11 @@ impl PduEvent {
_ => vec![], _ => vec![],
}; };
let old_content = self.content.as_object_mut().unwrap(); // TODO error let old_content = self
.content
.as_object_mut()
.ok_or(Error::BadDatabase("PDU has invalid content"))?;
let mut new_content = serde_json::Map::new(); let mut new_content = serde_json::Map::new();
for key in allowed { for key in allowed {
@ -71,21 +76,23 @@ impl PduEvent {
); );
self.content = new_content.into(); self.content = new_content.into();
Ok(())
} }
pub fn to_room_event(&self) -> EventJson<RoomEvent> { pub fn to_room_event(&self) -> EventJson<RoomEvent> {
// Can only fail in rare circumstances that won't ever happen here, see let json = serde_json::to_string(&self).expect("PDUs are always valid");
// https://docs.rs/serde_json/1.0.50/serde_json/fn.to_string.html serde_json::from_str::<EventJson<RoomEvent>>(&json)
let json = serde_json::to_string(&self).unwrap(); .expect("EventJson::from_str always works")
// EventJson's deserialize implementation always returns `Ok(...)`
serde_json::from_str::<EventJson<RoomEvent>>(&json).unwrap()
} }
pub fn to_state_event(&self) -> EventJson<StateEvent> { pub fn to_state_event(&self) -> EventJson<StateEvent> {
let json = serde_json::to_string(&self).unwrap(); let json = serde_json::to_string(&self).expect("PDUs are always valid");
serde_json::from_str::<EventJson<StateEvent>>(&json).unwrap() serde_json::from_str::<EventJson<StateEvent>>(&json)
.expect("EventJson::from_str always works")
} }
pub fn to_stripped_state_event(&self) -> EventJson<AnyStrippedStateEvent> { pub fn to_stripped_state_event(&self) -> EventJson<AnyStrippedStateEvent> {
let json = serde_json::to_string(&self).unwrap(); let json = serde_json::to_string(&self).expect("PDUs are always valid");
serde_json::from_str::<EventJson<AnyStrippedStateEvent>>(&json).unwrap() serde_json::from_str::<EventJson<AnyStrippedStateEvent>>(&json)
.expect("EventJson::from_str always works")
} }
} }

View File

@ -1,4 +1,4 @@
use crate::utils; use crate::{utils, Error};
use log::warn; use log::warn;
use rocket::{ use rocket::{
data::{Data, FromData, FromDataFuture, Transform, TransformFuture, Transformed}, data::{Data, FromData, FromDataFuture, Transform, TransformFuture, Transformed},
@ -42,7 +42,7 @@ impl<'a, T: Endpoint> FromData<'a> for Ruma<T> {
let data = rocket::try_outcome!(outcome.owned()); let data = rocket::try_outcome!(outcome.owned());
let (user_id, device_id) = if T::METADATA.requires_authentication { let (user_id, device_id) = if T::METADATA.requires_authentication {
let db = request.guard::<State<'_, crate::Database>>().await.unwrap(); let db = request.guard::<State<'_, crate::Database>>().await.expect("database was loaded");
// Get token from header or query value // Get token from header or query value
let token = match request let token = match request
@ -108,32 +108,24 @@ impl<T> Deref for Ruma<T> {
} }
/// This struct converts ruma responses into rocket http responses. /// This struct converts ruma responses into rocket http responses.
pub struct MatrixResult<T, E = ruma::api::client::Error>(pub std::result::Result<T, E>); pub type ConduitResult<T> = std::result::Result<RumaResponse<T>, Error>;
impl<T, E> TryInto<http::Response<Vec<u8>>> for MatrixResult<T, E> pub struct RumaResponse<T: TryInto<http::Response<Vec<u8>>>>(pub T);
where
T: TryInto<http::Response<Vec<u8>>>,
E: Into<http::Response<Vec<u8>>>,
{
type Error = T::Error;
fn try_into(self) -> Result<http::Response<Vec<u8>>, T::Error> { impl<T: TryInto<http::Response<Vec<u8>>>> From<T> for RumaResponse<T> {
match self.0 { fn from(t: T) -> Self {
Ok(t) => t.try_into(), Self(t)
Err(e) => Ok(e.into()),
}
} }
} }
#[rocket::async_trait] #[rocket::async_trait]
impl<'r, T, E> Responder<'r> for MatrixResult<T, E> impl<'r, T> Responder<'r> for RumaResponse<T>
where where
T: Send + TryInto<http::Response<Vec<u8>>>, T: Send + TryInto<http::Response<Vec<u8>>>,
T::Error: Send, T::Error: Send,
E: Into<http::Response<Vec<u8>>> + Send,
{ {
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> { async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
let http_response: Result<http::Response<_>, _> = self.try_into(); let http_response: Result<http::Response<_>, _> = self.0.try_into();
match http_response { match http_response {
Ok(http_response) => { Ok(http_response) => {
let mut response = rocket::response::Response::build(); let mut response = rocket::response::Response::build();
@ -165,11 +157,3 @@ where
} }
} }
} }
impl<T, E> Deref for MatrixResult<T, E> {
type Target = Result<T, E>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@ -1,4 +1,3 @@
use crate::Result;
use argon2::{Config, Variant}; use argon2::{Config, Variant};
use rand::prelude::*; use rand::prelude::*;
use std::{ use std::{
@ -9,39 +8,38 @@ use std::{
pub fn millis_since_unix_epoch() -> u64 { pub fn millis_since_unix_epoch() -> u64 {
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.unwrap() .expect("time is valid")
.as_millis() as u64 .as_millis() as u64
} }
pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> { pub fn increment(old: Option<&[u8]>) -> Option<Vec<u8>> {
let number = match old { let number = match old.map(|bytes| bytes.try_into()) {
Some(bytes) => { Some(Ok(bytes)) => {
let array: [u8; 8] = bytes.try_into().unwrap(); let number = u64::from_be_bytes(bytes);
let number = u64::from_be_bytes(array);
number + 1 number + 1
} }
None => 1, // Start at one. since 0 should return the first event in the db _ => 1, // Start at one. since 0 should return the first event in the db
}; };
Some(number.to_be_bytes().to_vec()) Some(number.to_be_bytes().to_vec())
} }
pub fn generate_keypair(old: Option<&[u8]>) -> Option<Vec<u8>> { pub fn generate_keypair(old: Option<&[u8]>) -> Option<Vec<u8>> {
Some( Some(old.map(|s| s.to_vec()).unwrap_or_else(|| {
old.map(|s| s.to_vec()) ruma::signatures::Ed25519KeyPair::generate()
.unwrap_or_else(|| ruma::signatures::Ed25519KeyPair::generate().unwrap()), .expect("Ed25519KeyPair generation always works (?)")
) }))
} }
/// Parses the bytes into an u64. /// Parses the bytes into an u64.
pub fn u64_from_bytes(bytes: &[u8]) -> u64 { pub fn u64_from_bytes(bytes: &[u8]) -> Result<u64, std::array::TryFromSliceError> {
let array: [u8; 8] = bytes.try_into().expect("bytes are valid u64"); let array: [u8; 8] = bytes.try_into()?;
u64::from_be_bytes(array) Ok(u64::from_be_bytes(array))
} }
/// Parses the bytes into a string. /// Parses the bytes into a string.
pub fn string_from_bytes(bytes: &[u8]) -> Result<String> { pub fn string_from_bytes(bytes: &[u8]) -> Result<String, std::string::FromUtf8Error> {
Ok(String::from_utf8(bytes.to_vec())?) String::from_utf8(bytes.to_vec())
} }
pub fn random_string(length: usize) -> String { pub fn random_string(length: usize) -> String {
@ -52,7 +50,7 @@ pub fn random_string(length: usize) -> String {
} }
/// Calculate a new hash for the given password /// Calculate a new hash for the given password
pub fn calculate_hash(password: &str) -> std::result::Result<String, argon2::Error> { pub fn calculate_hash(password: &str) -> Result<String, argon2::Error> {
let hashing_config = Config { let hashing_config = Config {
variant: Variant::Argon2id, variant: Variant::Argon2id,
..Default::default() ..Default::default()