refactor: better error handling
This commit is contained in:
parent
80935c5826
commit
2368a90584
15 changed files with 2062 additions and 1909 deletions
|
@ -1,5 +1,6 @@
|
|||
use crate::{utils, Error, Result};
|
||||
use ruma::{
|
||||
api::client::error::ErrorKind,
|
||||
events::{collections::only::Event as EduEvent, EventJson, EventType},
|
||||
identifiers::{RoomId, UserId},
|
||||
};
|
||||
|
@ -20,7 +21,10 @@ impl AccountData {
|
|||
globals: &super::globals::Globals,
|
||||
) -> Result<()> {
|
||||
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());
|
||||
|
||||
|
@ -62,9 +66,10 @@ impl AccountData {
|
|||
key.push(0xff);
|
||||
key.extend_from_slice(kind.to_string().as_bytes());
|
||||
|
||||
self.roomuserdataid_accountdata
|
||||
.insert(key, &*serde_json::to_string(&json)?)
|
||||
.unwrap();
|
||||
self.roomuserdataid_accountdata.insert(
|
||||
key,
|
||||
&*serde_json::to_string(&json).expect("Map::to_string always works"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -109,17 +114,22 @@ impl AccountData {
|
|||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||
.map(|(k, v)| {
|
||||
Ok::<_, Error>((
|
||||
EventType::try_from(utils::string_from_bytes(
|
||||
k.rsplit(|&b| b == 0xff)
|
||||
.next()
|
||||
.ok_or(Error::BadDatabase("roomuserdataid is invalid"))?,
|
||||
)?)
|
||||
.map_err(|_| Error::BadDatabase("roomuserdataid is invalid"))?,
|
||||
serde_json::from_slice::<EventJson<EduEvent>>(&v).unwrap(),
|
||||
EventType::try_from(
|
||||
utils::string_from_bytes(
|
||||
k.rsplit(|&b| b == 0xff)
|
||||
.next()
|
||||
.ok_or(Error::BadDatabase("RoomUserData ID in db is invalid."))?,
|
||||
)
|
||||
.map_err(|_| Error::BadDatabase("RoomUserData ID in db is invalid."))?,
|
||||
)
|
||||
.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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Result;
|
||||
use crate::{Error, Result};
|
||||
use ruma::events::EventJson;
|
||||
|
||||
pub struct GlobalEdus {
|
||||
|
@ -21,7 +21,10 @@ impl GlobalEdus {
|
|||
.rev()
|
||||
.filter_map(|r| r.ok())
|
||||
.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
|
||||
|
@ -32,8 +35,10 @@ impl GlobalEdus {
|
|||
presence_id.push(0xff);
|
||||
presence_id.extend_from_slice(&presence.sender.to_string().as_bytes());
|
||||
|
||||
self.presenceid_presence
|
||||
.insert(presence_id, &*serde_json::to_string(&presence)?)?;
|
||||
self.presenceid_presence.insert(
|
||||
presence_id,
|
||||
&*serde_json::to_string(&presence).expect("PresenceEvent can be serialized"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -50,6 +55,9 @@ impl GlobalEdus {
|
|||
.presenceid_presence
|
||||
.range(&*first_possible_edu..)
|
||||
.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."))?)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{utils, Result};
|
||||
use crate::{utils, Error, Result};
|
||||
|
||||
pub const COUNTER: &str = "c";
|
||||
|
||||
|
@ -11,17 +11,16 @@ pub struct 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(
|
||||
&*globals
|
||||
.update_and_fetch("keypair", utils::generate_keypair)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
.update_and_fetch("keypair", utils::generate_keypair)?
|
||||
.expect("utils::generate_keypair always returns Some"),
|
||||
"key1".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
.map_err(|_| Error::BadDatabase("Private or public keys are invalid."))?;
|
||||
|
||||
Self {
|
||||
Ok(Self {
|
||||
globals,
|
||||
keypair,
|
||||
reqwest_client: reqwest::Client::new(),
|
||||
|
@ -30,7 +29,7 @@ impl Globals {
|
|||
.unwrap_or("localhost")
|
||||
.to_owned(),
|
||||
registration_disabled: config.get_bool("registration_disabled").unwrap_or(false),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns this server's keypair.
|
||||
|
@ -49,14 +48,15 @@ impl Globals {
|
|||
.globals
|
||||
.update_and_fetch(COUNTER, utils::increment)?
|
||||
.expect("utils::increment will always put in a value"),
|
||||
))
|
||||
)
|
||||
.map_err(|_| Error::BadDatabase("Count has invalid bytes."))?)
|
||||
}
|
||||
|
||||
pub fn current_count(&self) -> Result<u64> {
|
||||
Ok(self
|
||||
.globals
|
||||
.get(COUNTER)?
|
||||
.map_or(0_u64, |bytes| utils::u64_from_bytes(&bytes)))
|
||||
self.globals.get(COUNTER)?.map_or(Ok(0_u64), |bytes| {
|
||||
Ok(utils::u64_from_bytes(&bytes)
|
||||
.map_err(|_| Error::BadDatabase("Count has invalid bytes."))?)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn server_name(&self) -> &str {
|
||||
|
|
|
@ -43,16 +43,21 @@ impl Media {
|
|||
let content_type = utils::string_from_bytes(
|
||||
parts
|
||||
.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
|
||||
.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() {
|
||||
None
|
||||
} 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())))
|
||||
|
@ -89,16 +94,21 @@ impl Media {
|
|||
let content_type = utils::string_from_bytes(
|
||||
parts
|
||||
.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
|
||||
.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() {
|
||||
None
|
||||
} 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())))
|
||||
|
@ -110,16 +120,21 @@ impl Media {
|
|||
let content_type = utils::string_from_bytes(
|
||||
parts
|
||||
.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
|
||||
.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() {
|
||||
None
|
||||
} 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) {
|
||||
|
|
|
@ -5,6 +5,7 @@ pub use edus::RoomEdus;
|
|||
use crate::{utils, Error, PduEvent, Result};
|
||||
use log::error;
|
||||
use ruma::{
|
||||
api::client::error::ErrorKind,
|
||||
events::{
|
||||
room::{
|
||||
join_rules, member,
|
||||
|
@ -61,30 +62,34 @@ impl Rooms {
|
|||
.roomstateid_pdu
|
||||
.scan_prefix(&room_id.to_string().as_bytes())
|
||||
.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?;
|
||||
hashmap.insert(
|
||||
(
|
||||
pdu.kind.clone(),
|
||||
pdu.state_key
|
||||
.clone()
|
||||
.expect("state events have a state key"),
|
||||
),
|
||||
pdu,
|
||||
);
|
||||
let state_key = pdu.state_key.clone().ok_or(Error::BadDatabase(
|
||||
"Room state contains event without state_key.",
|
||||
))?;
|
||||
hashmap.insert((pdu.kind.clone(), state_key), pdu);
|
||||
}
|
||||
Ok(hashmap)
|
||||
}
|
||||
|
||||
/// Returns the `count` of this pdu's id.
|
||||
pub fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<u64>> {
|
||||
Ok(self
|
||||
.eventid_pduid
|
||||
self.eventid_pduid
|
||||
.get(event_id.to_string().as_bytes())?
|
||||
.map(|pdu_id| {
|
||||
utils::u64_from_bytes(&pdu_id[pdu_id.len() - mem::size_of::<u64>()..pdu_id.len()])
|
||||
}))
|
||||
.map_or(Ok(None), |pdu_id| {
|
||||
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.
|
||||
|
@ -92,11 +97,12 @@ impl Rooms {
|
|||
self.eventid_pduid
|
||||
.get(event_id.to_string().as_bytes())?
|
||||
.map_or(Ok(None), |pdu_id| {
|
||||
Ok(Some(serde_json::from_slice(
|
||||
&self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase(
|
||||
"eventid_pduid points to nonexistent pdu",
|
||||
))?,
|
||||
)?))
|
||||
Ok(Some(
|
||||
serde_json::from_slice(&self.pduid_pdu.get(pdu_id)?.ok_or(
|
||||
Error::BadDatabase("eventid_pduid points to nonexistent pdu."),
|
||||
)?)
|
||||
.map_err(|_| Error::BadDatabase("Invalid PDU in db."))?,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -112,28 +118,37 @@ impl Rooms {
|
|||
self.eventid_pduid
|
||||
.get(event_id.to_string().as_bytes())?
|
||||
.map_or(Ok(None), |pdu_id| {
|
||||
Ok(Some(serde_json::from_slice(
|
||||
&self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase(
|
||||
"eventid_pduid points to nonexistent pdu",
|
||||
))?,
|
||||
)?))
|
||||
Ok(Some(
|
||||
serde_json::from_slice(&self.pduid_pdu.get(pdu_id)?.ok_or(
|
||||
Error::BadDatabase("eventid_pduid points to nonexistent pdu."),
|
||||
)?)
|
||||
.map_err(|_| Error::BadDatabase("Invalid PDU in db."))?,
|
||||
))
|
||||
})
|
||||
}
|
||||
/// Returns the pdu.
|
||||
pub fn get_pdu_from_id(&self, pdu_id: &IVec) -> Result<Option<PduEvent>> {
|
||||
self.pduid_pdu
|
||||
.get(pdu_id)?
|
||||
.map_or(Ok(None), |pdu| Ok(Some(serde_json::from_slice(&pdu)?)))
|
||||
self.pduid_pdu.get(pdu_id)?.map_or(Ok(None), |pdu| {
|
||||
Ok(Some(
|
||||
serde_json::from_slice(&pdu)
|
||||
.map_err(|_| Error::BadDatabase("Invalid PDU in db."))?,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the pdu.
|
||||
pub fn replace_pdu(&self, pdu_id: &IVec, pdu: &PduEvent) -> Result<()> {
|
||||
/// Removes a pdu and creates a new one with the same id.
|
||||
fn replace_pdu(&self, pdu_id: &IVec, pdu: &PduEvent) -> Result<()> {
|
||||
if self.pduid_pdu.get(&pdu_id)?.is_some() {
|
||||
self.pduid_pdu
|
||||
.insert(&pdu_id, &*serde_json::to_string(pdu)?)?;
|
||||
self.pduid_pdu.insert(
|
||||
&pdu_id,
|
||||
&*serde_json::to_string(pdu).expect("PduEvent::to_string always works"),
|
||||
)?;
|
||||
Ok(())
|
||||
} 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
|
||||
.scan_prefix(prefix)
|
||||
.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?);
|
||||
}
|
||||
|
@ -214,174 +234,203 @@ impl Rooms {
|
|||
Ok(
|
||||
serde_json::from_value::<EventJson<PowerLevelsEventContent>>(
|
||||
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
|
||||
.room_state(&room_id)?
|
||||
.get(&(EventType::RoomMember, sender.to_string()))
|
||||
.map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| {
|
||||
Ok(
|
||||
serde_json::from_value::<EventJson<member::MemberEventContent>>(
|
||||
pdu.content.clone(),
|
||||
)?
|
||||
.deserialize()?
|
||||
.membership,
|
||||
let sender_membership = self
|
||||
.room_state(&room_id)?
|
||||
.get(&(EventType::RoomMember, sender.to_string()))
|
||||
.map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| {
|
||||
Ok(
|
||||
serde_json::from_value::<EventJson<member::MemberEventContent>>(
|
||||
pdu.content.clone(),
|
||||
)
|
||||
.expect("EventJson::from_value always works.")
|
||||
.deserialize()
|
||||
.map_err(|_| Error::BadDatabase("Invalid Member event in db."))?
|
||||
.membership,
|
||||
)
|
||||
})?;
|
||||
|
||||
let sender_power = power_levels.users.get(&sender).map_or_else(
|
||||
|| {
|
||||
if sender_membership != member::MembershipState::Join {
|
||||
None
|
||||
} else {
|
||||
Some(&power_levels.users_default)
|
||||
}
|
||||
},
|
||||
// If it's okay, wrap with Some(_)
|
||||
Some,
|
||||
);
|
||||
|
||||
if !match event_type {
|
||||
EventType::RoomMember => {
|
||||
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 sender_power = power_levels.users.get(&sender).map_or_else(
|
||||
|| {
|
||||
if sender_membership != member::MembershipState::Join {
|
||||
None
|
||||
} else {
|
||||
Some(&power_levels.users_default)
|
||||
}
|
||||
},
|
||||
// If it's okay, wrap with Some(_)
|
||||
Some,
|
||||
);
|
||||
let current_membership = self
|
||||
.room_state(&room_id)?
|
||||
.get(&(EventType::RoomMember, target_user_id.to_string()))
|
||||
.map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| {
|
||||
Ok(
|
||||
serde_json::from_value::<EventJson<member::MemberEventContent>>(
|
||||
pdu.content.clone(),
|
||||
)
|
||||
.expect("EventJson::from_value always works.")
|
||||
.deserialize()
|
||||
.map_err(|_| Error::BadDatabase("Invalid Member event in db."))?
|
||||
.membership,
|
||||
)
|
||||
})?;
|
||||
|
||||
if !match event_type {
|
||||
EventType::RoomMember => {
|
||||
let target_user_id = UserId::try_from(&**state_key)?;
|
||||
let target_membership = serde_json::from_value::<
|
||||
EventJson<member::MemberEventContent>,
|
||||
>(content.clone())
|
||||
.expect("EventJson::from_value always works.")
|
||||
.deserialize()
|
||||
.map_err(|_| Error::BadDatabase("Invalid Member event in db."))?
|
||||
.membership;
|
||||
|
||||
let current_membership = self
|
||||
.room_state(&room_id)?
|
||||
.get(&(EventType::RoomMember, target_user_id.to_string()))
|
||||
.map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| {
|
||||
Ok(serde_json::from_value::<
|
||||
EventJson<member::MemberEventContent>,
|
||||
>(pdu.content.clone())?
|
||||
.deserialize()?
|
||||
.membership)
|
||||
})?;
|
||||
let target_power = power_levels.users.get(&target_user_id).map_or_else(
|
||||
|| {
|
||||
if target_membership != member::MembershipState::Join {
|
||||
None
|
||||
} else {
|
||||
Some(&power_levels.users_default)
|
||||
}
|
||||
},
|
||||
// If it's okay, wrap with Some(_)
|
||||
Some,
|
||||
);
|
||||
|
||||
let target_membership = serde_json::from_value::<
|
||||
EventJson<member::MemberEventContent>,
|
||||
>(content.clone())?
|
||||
.deserialize()?
|
||||
.membership;
|
||||
|
||||
let target_power = power_levels.users.get(&target_user_id).map_or_else(
|
||||
|| {
|
||||
if target_membership != member::MembershipState::Join {
|
||||
None
|
||||
} else {
|
||||
Some(&power_levels.users_default)
|
||||
}
|
||||
},
|
||||
// If it's okay, wrap with Some(_)
|
||||
Some,
|
||||
);
|
||||
|
||||
let join_rules = self
|
||||
.room_state(&room_id)?
|
||||
let join_rules =
|
||||
self.room_state(&room_id)?
|
||||
.get(&(EventType::RoomJoinRules, "".to_owned()))
|
||||
.map_or(join_rules::JoinRule::Public, |pdu| {
|
||||
serde_json::from_value::<
|
||||
.map_or(Ok::<_, Error>(join_rules::JoinRule::Public), |pdu| {
|
||||
Ok(serde_json::from_value::<
|
||||
EventJson<join_rules::JoinRulesEventContent>,
|
||||
>(pdu.content.clone())
|
||||
.unwrap()
|
||||
.expect("EventJson::from_value always works.")
|
||||
.deserialize()
|
||||
.unwrap()
|
||||
.join_rule
|
||||
});
|
||||
.map_err(|_| {
|
||||
Error::BadDatabase("Database contains invalid JoinRules event")
|
||||
})?
|
||||
.join_rule)
|
||||
})?;
|
||||
|
||||
let authorized = if target_membership == member::MembershipState::Join {
|
||||
let mut prev_events = prev_events.iter();
|
||||
let prev_event = self
|
||||
.get_pdu(prev_events.next().ok_or(Error::BadRequest(
|
||||
"membership can't be the first event",
|
||||
))?)?
|
||||
.ok_or(Error::BadDatabase("pdu leave points to valid event"))?;
|
||||
if prev_event.kind == EventType::RoomCreate
|
||||
&& prev_event.prev_events.is_empty()
|
||||
{
|
||||
true
|
||||
} else if sender != target_user_id {
|
||||
false
|
||||
} else if let member::MembershipState::Ban = current_membership {
|
||||
false
|
||||
} else {
|
||||
join_rules == join_rules::JoinRule::Invite
|
||||
&& (current_membership == member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Invite)
|
||||
|| join_rules == join_rules::JoinRule::Public
|
||||
}
|
||||
} else if target_membership == member::MembershipState::Invite {
|
||||
if let Some(third_party_invite_json) = content.get("third_party_invite")
|
||||
{
|
||||
if current_membership == member::MembershipState::Ban {
|
||||
false
|
||||
} else {
|
||||
let _third_party_invite =
|
||||
serde_json::from_value::<member::ThirdPartyInvite>(
|
||||
third_party_invite_json.clone(),
|
||||
)?;
|
||||
todo!("handle third party invites");
|
||||
}
|
||||
} else if sender_membership != member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Ban
|
||||
{
|
||||
false
|
||||
} else {
|
||||
sender_power
|
||||
.filter(|&p| p >= &power_levels.invite)
|
||||
.is_some()
|
||||
}
|
||||
} else if target_membership == member::MembershipState::Leave {
|
||||
if sender == target_user_id {
|
||||
current_membership == member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Invite
|
||||
} else if sender_membership != member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Ban
|
||||
&& sender_power.filter(|&p| p < &power_levels.ban).is_some()
|
||||
{
|
||||
false
|
||||
} else {
|
||||
sender_power.filter(|&p| p >= &power_levels.kick).is_some()
|
||||
&& target_power < sender_power
|
||||
}
|
||||
} else if target_membership == member::MembershipState::Ban {
|
||||
if sender_membership != member::MembershipState::Join {
|
||||
false
|
||||
} else {
|
||||
sender_power.filter(|&p| p >= &power_levels.ban).is_some()
|
||||
&& target_power < sender_power
|
||||
}
|
||||
} else {
|
||||
let authorized = if target_membership == member::MembershipState::Join {
|
||||
let mut prev_events = prev_events.iter();
|
||||
let prev_event = self
|
||||
.get_pdu(prev_events.next().ok_or(Error::BadRequest(
|
||||
ErrorKind::Unknown,
|
||||
"Membership can't be the first event",
|
||||
))?)?
|
||||
.ok_or(Error::BadDatabase("PDU leaf points to invalid event!"))?;
|
||||
if prev_event.kind == EventType::RoomCreate
|
||||
&& prev_event.prev_events.is_empty()
|
||||
{
|
||||
true
|
||||
} else if sender != target_user_id {
|
||||
false
|
||||
};
|
||||
|
||||
if authorized {
|
||||
// Update our membership info
|
||||
self.update_membership(&room_id, &target_user_id, &target_membership)?;
|
||||
} else if let member::MembershipState::Ban = current_membership {
|
||||
false
|
||||
} else {
|
||||
join_rules == join_rules::JoinRule::Invite
|
||||
&& (current_membership == member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Invite)
|
||||
|| join_rules == join_rules::JoinRule::Public
|
||||
}
|
||||
} else if target_membership == member::MembershipState::Invite {
|
||||
if let Some(third_party_invite_json) = content.get("third_party_invite") {
|
||||
if current_membership == member::MembershipState::Ban {
|
||||
false
|
||||
} else {
|
||||
let _third_party_invite =
|
||||
serde_json::from_value::<member::ThirdPartyInvite>(
|
||||
third_party_invite_json.clone(),
|
||||
)
|
||||
.map_err(|_| {
|
||||
Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"ThirdPartyInvite is invalid",
|
||||
)
|
||||
})?;
|
||||
todo!("handle third party invites");
|
||||
}
|
||||
} else if sender_membership != member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Ban
|
||||
{
|
||||
false
|
||||
} else {
|
||||
sender_power
|
||||
.filter(|&p| p >= &power_levels.invite)
|
||||
.is_some()
|
||||
}
|
||||
} else if target_membership == member::MembershipState::Leave {
|
||||
if sender == target_user_id {
|
||||
current_membership == member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Invite
|
||||
} else if sender_membership != member::MembershipState::Join
|
||||
|| current_membership == member::MembershipState::Ban
|
||||
&& sender_power.filter(|&p| p < &power_levels.ban).is_some()
|
||||
{
|
||||
false
|
||||
} else {
|
||||
sender_power.filter(|&p| p >= &power_levels.kick).is_some()
|
||||
&& target_power < sender_power
|
||||
}
|
||||
} else if target_membership == member::MembershipState::Ban {
|
||||
if sender_membership != member::MembershipState::Join {
|
||||
false
|
||||
} else {
|
||||
sender_power.filter(|&p| p >= &power_levels.ban).is_some()
|
||||
&& target_power < sender_power
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
authorized
|
||||
if authorized {
|
||||
// Update our membership info
|
||||
self.update_membership(&room_id, &target_user_id, &target_membership)?;
|
||||
}
|
||||
EventType::RoomCreate => prev_events.is_empty(),
|
||||
// Not allow any of the following events if the sender is not joined.
|
||||
_ if sender_membership != member::MembershipState::Join => false,
|
||||
|
||||
_ => {
|
||||
// TODO
|
||||
sender_power.unwrap_or(&power_levels.users_default)
|
||||
>= &power_levels.state_default
|
||||
}
|
||||
} {
|
||||
error!("Unauthorized");
|
||||
// Not authorized
|
||||
return Err(Error::BadRequest("event not authorized"));
|
||||
authorized
|
||||
}
|
||||
EventType::RoomCreate => prev_events.is_empty(),
|
||||
// Not allow any of the following events if the sender is not joined.
|
||||
_ if sender_membership != member::MembershipState::Join => false,
|
||||
|
||||
_ => {
|
||||
// TODO
|
||||
sender_power.unwrap_or(&power_levels.users_default)
|
||||
>= &power_levels.state_default
|
||||
}
|
||||
} {
|
||||
error!("Unauthorized");
|
||||
// Not authorized
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Forbidden,
|
||||
"Event is not authorized",
|
||||
));
|
||||
}
|
||||
} 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
|
||||
|
@ -410,14 +459,14 @@ impl Rooms {
|
|||
origin: globals.server_name().to_owned(),
|
||||
origin_server_ts: utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("this only fails many years in the future"),
|
||||
.expect("time is valid"),
|
||||
kind: event_type.clone(),
|
||||
content: content.clone(),
|
||||
state_key,
|
||||
prev_events,
|
||||
depth: depth
|
||||
.try_into()
|
||||
.expect("depth can overflow and should be deprecated..."),
|
||||
.map_err(|_| Error::BadDatabase("Depth is invalid"))?,
|
||||
auth_events: Vec::new(),
|
||||
redacts: redacts.clone(),
|
||||
unsigned,
|
||||
|
@ -430,18 +479,20 @@ impl Rooms {
|
|||
// Generate event id
|
||||
pdu.event_id = EventId::try_from(&*format!(
|
||||
"${}",
|
||||
ruma::signatures::reference_hash(&serde_json::to_value(&pdu)?)
|
||||
.expect("ruma can calculate reference hashes")
|
||||
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'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(
|
||||
globals.server_name(),
|
||||
globals.keypair(),
|
||||
&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)?;
|
||||
|
||||
|
@ -473,8 +524,15 @@ impl Rooms {
|
|||
// TODO: Reason
|
||||
let _reason = serde_json::from_value::<
|
||||
EventJson<redaction::RedactionEventContent>,
|
||||
>(content)?
|
||||
.deserialize()?
|
||||
>(content)
|
||||
.expect("EventJson::from_value always works.")
|
||||
.deserialize()
|
||||
.map_err(|_| {
|
||||
Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Invalid redaction event content.",
|
||||
)
|
||||
})?
|
||||
.reason;
|
||||
|
||||
self.redact_pdu(&redact_id)?;
|
||||
|
@ -528,7 +586,10 @@ impl Rooms {
|
|||
})
|
||||
.filter_map(|r| r.ok())
|
||||
.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
|
||||
|
@ -552,7 +613,10 @@ impl Rooms {
|
|||
.rev()
|
||||
.filter_map(|r| r.ok())
|
||||
.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
|
||||
|
@ -575,7 +639,10 @@ impl Rooms {
|
|||
.range(current..)
|
||||
.filter_map(|r| r.ok())
|
||||
.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.
|
||||
|
@ -583,12 +650,15 @@ impl Rooms {
|
|||
if let Some(pdu_id) = self.get_pdu_id(event_id)? {
|
||||
let mut pdu = self
|
||||
.get_pdu_from_id(&pdu_id)?
|
||||
.ok_or(Error::BadDatabase("pduid points to invalid pdu"))?;
|
||||
pdu.redact();
|
||||
.ok_or(Error::BadDatabase("PDU ID points to invalid PDU."))?;
|
||||
pdu.redact()?;
|
||||
self.replace_pdu(&pdu_id, &pdu)?;
|
||||
Ok(())
|
||||
} 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
|
||||
.alias_roomid
|
||||
.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() {
|
||||
self.aliasid_alias.remove(key?)?;
|
||||
|
@ -678,7 +751,9 @@ impl Rooms {
|
|||
self.alias_roomid
|
||||
.get(alias.alias())?
|
||||
.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
|
||||
.scan_prefix(prefix)
|
||||
.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<()> {
|
||||
|
@ -707,10 +785,10 @@ impl Rooms {
|
|||
}
|
||||
|
||||
pub fn public_rooms(&self) -> impl Iterator<Item = Result<RoomId>> {
|
||||
self.publicroomids
|
||||
.iter()
|
||||
.keys()
|
||||
.map(|bytes| Ok(RoomId::try_from(utils::string_from_bytes(&bytes?)?)?))
|
||||
self.publicroomids.iter().keys().map(|bytes| {
|
||||
Ok(serde_json::from_slice(&bytes?)
|
||||
.map_err(|_| Error::BadDatabase("Room ID in publicroomids is invalid."))?)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an iterator over all rooms a user joined.
|
||||
|
@ -719,12 +797,13 @@ impl Rooms {
|
|||
.scan_prefix(room_id.to_string())
|
||||
.values()
|
||||
.map(|key| {
|
||||
Ok(UserId::try_from(&*utils::string_from_bytes(
|
||||
Ok(serde_json::from_slice(
|
||||
&key?
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.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())
|
||||
.keys()
|
||||
.map(|key| {
|
||||
Ok(UserId::try_from(&*utils::string_from_bytes(
|
||||
Ok(serde_json::from_slice(
|
||||
&key?
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.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())
|
||||
.keys()
|
||||
.map(|key| {
|
||||
Ok(RoomId::try_from(&*utils::string_from_bytes(
|
||||
Ok(serde_json::from_slice(
|
||||
&key?
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.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())
|
||||
.keys()
|
||||
.map(|key| {
|
||||
Ok(RoomId::try_from(&*utils::string_from_bytes(
|
||||
Ok(serde_json::from_slice(
|
||||
&key?
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.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())
|
||||
.keys()
|
||||
.map(|key| {
|
||||
Ok(RoomId::try_from(&*utils::string_from_bytes(
|
||||
Ok(serde_json::from_slice(
|
||||
&key?
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.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."))?)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,10 @@ impl RoomEdus {
|
|||
.filter_map(|r| r.ok())
|
||||
.take_while(|key| key.starts_with(&prefix))
|
||||
.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
|
||||
|
@ -45,8 +48,10 @@ impl RoomEdus {
|
|||
room_latest_id.push(0xff);
|
||||
room_latest_id.extend_from_slice(&user_id.to_string().as_bytes());
|
||||
|
||||
self.roomlatestid_roomlatest
|
||||
.insert(room_latest_id, &*serde_json::to_string(&event)?)?;
|
||||
self.roomlatestid_roomlatest.insert(
|
||||
room_latest_id,
|
||||
&*serde_json::to_string(&event).expect("EduEvent::to_string always works"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -68,7 +73,10 @@ impl RoomEdus {
|
|||
.range(&*first_possible_edu..)
|
||||
.filter_map(|r| r.ok())
|
||||
.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
|
||||
|
@ -152,17 +160,21 @@ impl RoomEdus {
|
|||
.roomactiveid_userid
|
||||
.scan_prefix(&prefix)
|
||||
.keys()
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(|k| {
|
||||
utils::u64_from_bytes(
|
||||
k.split(|&c| c == 0xff)
|
||||
.nth(1)
|
||||
.expect("roomactive has valid timestamp and delimiters"),
|
||||
) < current_timestamp
|
||||
.map(|key| {
|
||||
let key = key?;
|
||||
Ok::<_, Error>((
|
||||
key.clone(),
|
||||
utils::u64_from_bytes(key.split(|&b| b == 0xff).nth(1).ok_or(
|
||||
Error::BadDatabase("RoomActive has invalid timestamp or delimiters."),
|
||||
)?)
|
||||
.map_err(|_| Error::BadDatabase("RoomActive has invalid timestamp bytes."))?,
|
||||
))
|
||||
})
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(|&(_, timestamp)| timestamp < current_timestamp)
|
||||
{
|
||||
// This is an outdated edu (time > timestamp)
|
||||
self.roomactiveid_userid.remove(outdated_edu)?;
|
||||
self.roomactiveid_userid.remove(outdated_edu.0)?;
|
||||
found_outdated = true;
|
||||
}
|
||||
|
||||
|
@ -187,7 +199,11 @@ impl RoomEdus {
|
|||
Ok(self
|
||||
.roomid_lastroomactiveupdate
|
||||
.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))
|
||||
}
|
||||
|
||||
|
@ -202,7 +218,11 @@ impl RoomEdus {
|
|||
.roomactiveid_userid
|
||||
.scan_prefix(prefix)
|
||||
.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?);
|
||||
}
|
||||
|
@ -230,9 +250,10 @@ impl RoomEdus {
|
|||
key.push(0xff);
|
||||
key.extend_from_slice(&user_id.to_string().as_bytes());
|
||||
|
||||
Ok(self
|
||||
.roomuserid_lastread
|
||||
.get(key)?
|
||||
.map(|v| utils::u64_from_bytes(&v)))
|
||||
self.roomuserid_lastread.get(key)?.map_or(Ok(None), |v| {
|
||||
Ok(Some(utils::u64_from_bytes(&v).map_err(|_| {
|
||||
Error::BadDatabase("Invalid private read marker bytes")
|
||||
})?))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,15 +43,51 @@ impl Uiaa {
|
|||
// Find out what the user completed
|
||||
match &**kind {
|
||||
"m.login.password" => {
|
||||
if auth_parameters["identifier"]["type"] != "m.id.user" {
|
||||
panic!("identifier not supported");
|
||||
let identifier = auth_parameters.get("identifier").ok_or(Error::BadRequest(
|
||||
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(
|
||||
auth_parameters["identifier"]["user"].as_str().unwrap(),
|
||||
globals.server_name(),
|
||||
)?;
|
||||
let password = auth_parameters["password"].as_str().unwrap();
|
||||
let username = identifier
|
||||
.get("user")
|
||||
.ok_or(Error::BadRequest(
|
||||
ErrorKind::MissingParam,
|
||||
"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
|
||||
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);
|
||||
|
||||
if !hash_matches {
|
||||
debug!("Invalid password.");
|
||||
uiaainfo.auth_error = Some(ruma::api::client::error::ErrorBody {
|
||||
kind: ErrorKind::Forbidden,
|
||||
message: "Invalid username or password.".to_owned(),
|
||||
|
@ -113,8 +148,10 @@ impl Uiaa {
|
|||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
if let Some(uiaainfo) = uiaainfo {
|
||||
self.userdeviceid_uiaainfo
|
||||
.insert(&userdeviceid, &*serde_json::to_string(&uiaainfo)?)?;
|
||||
self.userdeviceid_uiaainfo.insert(
|
||||
&userdeviceid,
|
||||
&*serde_json::to_string(&uiaainfo).expect("UiaaInfo::to_string always works"),
|
||||
)?;
|
||||
} else {
|
||||
self.userdeviceid_uiaainfo.remove(&userdeviceid)?;
|
||||
}
|
||||
|
@ -136,8 +173,12 @@ impl Uiaa {
|
|||
&self
|
||||
.userdeviceid_uiaainfo
|
||||
.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
|
||||
.session
|
||||
|
@ -145,7 +186,10 @@ impl Uiaa {
|
|||
.filter(|&s| s == session)
|
||||
.is_none()
|
||||
{
|
||||
return Err(Error::BadRequest("wrong session token"));
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Forbidden,
|
||||
"UIAA session token invalid.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(uiaainfo)
|
||||
|
|
|
@ -43,24 +43,29 @@ impl Users {
|
|||
.get(token)?
|
||||
.map_or(Ok(None), |bytes| {
|
||||
let mut parts = bytes.split(|&b| b == 0xff);
|
||||
let user_bytes = parts
|
||||
.next()
|
||||
.ok_or(Error::BadDatabase("token_userdeviceid value invalid"))?;
|
||||
let device_bytes = parts
|
||||
.next()
|
||||
.ok_or(Error::BadDatabase("token_userdeviceid value invalid"))?;
|
||||
let user_bytes = parts.next().ok_or(Error::BadDatabase(
|
||||
"token_userdeviceid value in db is invalid.",
|
||||
))?;
|
||||
let device_bytes = parts.next().ok_or(Error::BadDatabase(
|
||||
"token_userdeviceid value in db is invalid.",
|
||||
))?;
|
||||
|
||||
Ok(Some((
|
||||
UserId::try_from(utils::string_from_bytes(&user_bytes)?)?,
|
||||
utils::string_from_bytes(&device_bytes)?,
|
||||
serde_json::from_slice(&user_bytes).map_err(|_| {
|
||||
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.
|
||||
pub fn iter(&self) -> impl Iterator<Item = Result<UserId>> {
|
||||
self.userid_password.iter().keys().map(|r| {
|
||||
utils::string_from_bytes(&r?).and_then(|string| Ok(UserId::try_from(&*string)?))
|
||||
self.userid_password.iter().keys().map(|bytes| {
|
||||
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>> {
|
||||
self.userid_password
|
||||
.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.
|
||||
pub fn displayname(&self, user_id: &UserId) -> Result<Option<String>> {
|
||||
self.userid_displayname
|
||||
.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.
|
||||
|
@ -94,7 +107,11 @@ impl Users {
|
|||
pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<String>> {
|
||||
self.userid_avatarurl
|
||||
.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.
|
||||
|
@ -117,11 +134,8 @@ impl Users {
|
|||
token: &str,
|
||||
initial_device_display_name: Option<String>,
|
||||
) -> Result<()> {
|
||||
if !self.exists(user_id)? {
|
||||
return Err(Error::BadRequest(
|
||||
"tried to create device for nonexistent user",
|
||||
));
|
||||
}
|
||||
// This method should never be called for nonexistent users.
|
||||
assert!(self.exists(user_id)?);
|
||||
|
||||
let mut userdeviceid = user_id.to_string().as_bytes().to_vec();
|
||||
userdeviceid.push(0xff);
|
||||
|
@ -134,7 +148,8 @@ impl Users {
|
|||
display_name: initial_device_display_name,
|
||||
last_seen_ip: None, // TODO
|
||||
last_seen_ts: Some(SystemTime::now()),
|
||||
})?
|
||||
})
|
||||
.expect("Device::to_string never fails.")
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
|
@ -185,23 +200,22 @@ impl Users {
|
|||
&*bytes?
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.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.
|
||||
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();
|
||||
userdeviceid.push(0xff);
|
||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
// All devices have metadata
|
||||
if self.userdeviceid_metadata.get(&userdeviceid)?.is_none() {
|
||||
return Err(Error::BadRequest(
|
||||
"Tried to set token for nonexistent device",
|
||||
));
|
||||
}
|
||||
assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some());
|
||||
|
||||
// Remove old token
|
||||
if let Some(old_token) = self.userdeviceid_token.get(&userdeviceid)? {
|
||||
|
@ -228,19 +242,23 @@ impl Users {
|
|||
key.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
// All devices have metadata
|
||||
if self.userdeviceid_metadata.get(&key)?.is_none() {
|
||||
return Err(Error::BadRequest(
|
||||
"Tried to set token for nonexistent device",
|
||||
));
|
||||
}
|
||||
// Only existing devices should be able to call this.
|
||||
assert!(self.userdeviceid_metadata.get(&key)?.is_some());
|
||||
|
||||
key.push(0xff);
|
||||
// TODO: Use AlgorithmAndDeviceId::to_string when it's available (and update everything,
|
||||
// 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
|
||||
.insert(&key, &*serde_json::to_string(&one_time_key_value)?)?;
|
||||
self.onetimekeyid_onetimekeys.insert(
|
||||
&key,
|
||||
&*serde_json::to_string(&one_time_key_value)
|
||||
.expect("OneTimeKey::to_string always works"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -271,9 +289,11 @@ impl Users {
|
|||
&*key
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.next()
|
||||
.ok_or(Error::BadDatabase("onetimekeyid is invalid"))?,
|
||||
)?,
|
||||
serde_json::from_slice(&*value)?,
|
||||
.ok_or(Error::BadDatabase("OneTimeKeyId in db is invalid."))?,
|
||||
)
|
||||
.map_err(|_| Error::BadDatabase("OneTimeKeyId in db is invalid."))?,
|
||||
serde_json::from_slice(&*value)
|
||||
.map_err(|_| Error::BadDatabase("OneTimeKeys in db are invalid."))?,
|
||||
))
|
||||
})
|
||||
.transpose()
|
||||
|
@ -300,8 +320,9 @@ impl Users {
|
|||
&*bytes?
|
||||
.rsplit(|&b| b == 0xff)
|
||||
.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,
|
||||
)
|
||||
})
|
||||
|
@ -323,8 +344,10 @@ impl Users {
|
|||
userdeviceid.push(0xff);
|
||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
self.userdeviceid_devicekeys
|
||||
.insert(&userdeviceid, &*serde_json::to_string(&device_keys)?)?;
|
||||
self.userdeviceid_devicekeys.insert(
|
||||
&userdeviceid,
|
||||
&*serde_json::to_string(&device_keys).expect("DeviceKeys::to_string always works"),
|
||||
)?;
|
||||
|
||||
self.devicekeychangeid_userid
|
||||
.insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?;
|
||||
|
@ -344,14 +367,21 @@ impl Users {
|
|||
self.userdeviceid_devicekeys
|
||||
.scan_prefix(key)
|
||||
.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>> {
|
||||
self.devicekeychangeid_userid
|
||||
.range(since.to_be_bytes()..)
|
||||
.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(
|
||||
|
@ -366,9 +396,14 @@ impl Users {
|
|||
let userdeviceid = utils::string_from_bytes(
|
||||
key.rsplit(|&b| b == 0xff)
|
||||
.next()
|
||||
.ok_or(Error::BadDatabase("userdeviceid is invalid"))?,
|
||||
)?;
|
||||
Ok((userdeviceid, serde_json::from_slice(&*value)?))
|
||||
.ok_or(Error::BadDatabase("UserDeviceID in db is invalid."))?,
|
||||
)
|
||||
.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("content".to_owned(), content);
|
||||
|
||||
self.todeviceid_events
|
||||
.insert(&key, &*serde_json::to_string(&json)?)?;
|
||||
self.todeviceid_events.insert(
|
||||
&key,
|
||||
&*serde_json::to_string(&json).expect("Map::to_string always works"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -413,7 +450,10 @@ impl Users {
|
|||
|
||||
for result in self.todeviceid_events.scan_prefix(&prefix).take(max) {
|
||||
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)?;
|
||||
}
|
||||
|
||||
|
@ -430,12 +470,15 @@ impl Users {
|
|||
userdeviceid.push(0xff);
|
||||
userdeviceid.extend_from_slice(device_id.as_bytes());
|
||||
|
||||
if self.userdeviceid_metadata.get(&userdeviceid)?.is_none() {
|
||||
return Err(Error::BadRequest("device does not exist"));
|
||||
}
|
||||
// Only existing devices should be able to call this.
|
||||
assert!(self.userdeviceid_metadata.get(&userdeviceid)?.is_some());
|
||||
|
||||
self.userdeviceid_metadata
|
||||
.insert(userdeviceid, serde_json::to_string(device)?.as_bytes())?;
|
||||
self.userdeviceid_metadata.insert(
|
||||
userdeviceid,
|
||||
serde_json::to_string(device)
|
||||
.expect("Device::to_string always works")
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -448,7 +491,11 @@ impl Users {
|
|||
|
||||
self.userdeviceid_metadata
|
||||
.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>> {
|
||||
|
@ -458,6 +505,10 @@ impl Users {
|
|||
self.userdeviceid_metadata
|
||||
.scan_prefix(key)
|
||||
.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.")
|
||||
})?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue