diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..09ad525f --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Devin Ragotzy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index decc9685..b8eb1bc7 100644 --- a/README.md +++ b/README.md @@ -12,20 +12,24 @@ struct StateEvent { /// A mapping of event type and state_key to some value `T`, usually an `EventId`. pub type StateMap = BTreeMap<(EventType, String), T>; +/// A mapping of `EventId` to `T`, usually a `StateEvent`. +pub type EventMap = BTreeMap; struct StateResolution { - // Should any information be kept or should all of it be fetched from the - // StateStore trait? - event_map: BTreeMap, - - // fields for temp storage during resolution?? - /// The events that conflict and their auth chains. - conflicting_events: StateMap>, + // For now the StateResolution struct is empty. If "caching `event_map` between `resolve` calls + // ends up being more efficient it may have an `event_map` field. } impl StateResolution { - /// The point of this all. Resolve the conflicting set of . - fn resolve(&mut self, events: Vec>) -> StateMap { } + /// The point of this all, resolve the possibly conflicting sets of events. + pub fn resolve( + &self, + room_id: &RoomId, + room_version: &RoomVersionId, + state_sets: &[StateMap], + event_map: Option>, + store: &dyn StateStore, + ) -> Result; } @@ -34,11 +38,9 @@ trait StateStore { /// Return a single event based on the EventId. fn get_event(&self, event_id: &EventId) -> Result; - /// Returns the events that correspond to the `event_ids` sorted in the same order. - fn get_events(&self, event_ids: &[EventId]) -> Result, String>; - - /// Returns a Vec of the related auth events to the given `event`. - fn auth_event_ids(&self, room_id: &RoomId, event_ids: &[EventId]) -> Result, String>; + // There are 3 methods that have default implementations `get_events`, `auth_event_ids` and + // `auth_chain_diff`. Each could be overridden if the user has an optimization with their database of + // choice. } ``` diff --git a/src/event_auth.rs b/src/event_auth.rs index 8143fb4d..d6c9e57b 100644 --- a/src/event_auth.rs +++ b/src/event_auth.rs @@ -26,6 +26,8 @@ pub enum RedactAllowed { No, } +/// For the given event `kind` what are the relevant auth events +/// that are needed to authenticate this `content`. pub fn auth_types_for_event( kind: EventType, sender: &UserId, @@ -71,6 +73,10 @@ pub fn auth_types_for_event( auth_types } +/// Authenticate the incoming `event`. The steps of authentication are: +/// * check that the event is being authenticated for the correct room +/// * check that the events signatures are valid +/// * then there are checks for specific event types pub fn auth_check( room_version: &RoomVersionId, event: &StateEvent, @@ -107,7 +113,7 @@ pub fn auth_check( // TODO do_size_check is false when called by `iterative_auth_check` // do_size_check is also mostly accomplished by ruma with the exception of checking event_type, - // state_key, and json are below a certain size (255 and 65536 respectivly) + // state_key, and json are below a certain size (255 and 65536 respectively) // Implementation of https://matrix.org/docs/spec/rooms/v1#authorization-rules // @@ -129,7 +135,7 @@ pub fn auth_check( .get("room_version") .cloned() // synapse defaults to version 1 - .unwrap_or(serde_json::json!("1")), + .unwrap_or_else(|| serde_json::json!("1")), ) .is_err() { @@ -446,7 +452,7 @@ pub fn check_event_sender_in_room( ) } -/// Is the user allowed to send a specific event. +/// Is the user allowed to send a specific event based on the rooms power levels. pub fn can_send_event(event: &StateEvent, auth_events: &StateMap) -> Option { let ple = auth_events.get(&(EventType::RoomPowerLevels, Some("".into()))); @@ -661,6 +667,8 @@ pub fn check_membership(member_event: Option<&StateEvent>, state: MembershipStat } } +/// Helper function to fetch a field, `name`, from a "m.room.power_level" event's content. +/// or return `default` if no power level event is found or zero if no field matches `name`. pub fn get_named_level(auth_events: &StateMap, name: &str, default: i64) -> i64 { let power_level_event = auth_events.get(&(EventType::RoomPowerLevels, Some("".into()))); if let Some(pl) = power_level_event { @@ -675,6 +683,8 @@ pub fn get_named_level(auth_events: &StateMap, name: &str, default: } } +/// Helper function to fetch a users default power level from a "m.room.power_level" event's `users` +/// object. pub fn get_user_power_level(user_id: &UserId, auth_events: &StateMap) -> i64 { if let Some(pl) = auth_events.get(&(EventType::RoomPowerLevels, Some("".into()))) { if let Ok(content) = pl.deserialize_content::() @@ -706,6 +716,8 @@ pub fn get_user_power_level(user_id: &UserId, auth_events: &StateMap } } +/// Helper function to fetch the power level needed to send an event of type +/// `e_type` based on the rooms "m.room.power_level" event. pub fn get_send_level( e_type: EventType, state_key: Option, @@ -720,7 +732,7 @@ pub fn get_send_level( .events .get(&e_type) .cloned() - .unwrap_or(js_int::int!(50)) + .unwrap_or_else(|| js_int::int!(50)) .into(); let state_def: i64 = content.state_default.into(); let event_def: i64 = content.events_default.into(); diff --git a/src/lib.rs b/src/lib.rs index 503aff61..07e9c6d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -#![allow(clippy::or_fun_call)] - use std::{ cmp::Reverse, collections::{BTreeMap, BTreeSet, BinaryHeap}, @@ -266,7 +264,7 @@ impl StateResolution { "SEP {:?}", event_ids .iter() - .map(|i| i.map(ToString::to_string).unwrap_or("None".into())) + .map(|i| i.map(ToString::to_string).unwrap_or_else(|| "None".into())) .collect::>() ); @@ -560,7 +558,7 @@ impl StateResolution { tracing::debug!("event to check {:?}", event.event_id().to_string()); if event_auth::auth_check(room_version, &event, auth_events, false) - .ok_or("Auth check failed due to deserialization most likely".to_string()) + .ok_or_else(|| "Auth check failed due to deserialization most likely".to_string()) .map_err(Error::TempString)? { // add event to resolved state map @@ -713,7 +711,7 @@ impl StateResolution { while !state.is_empty() { // we just checked if it was empty so unwrap is fine let eid = state.pop().unwrap(); - graph.entry(eid.clone()).or_insert(vec![]); + graph.entry(eid.clone()).or_insert_with(Vec::new); // prefer the store to event as the store filters dedups the events // otherwise it seems we can loop forever for aid in self