Add license, update readme and add docs for event_auth functions

This commit is contained in:
Devin Ragotzy 2020-08-18 13:46:14 -04:00
parent 789c814089
commit ce2d5a0d9b
4 changed files with 54 additions and 23 deletions

19
LICENSE Normal file
View File

@ -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.

View File

@ -12,20 +12,24 @@ struct StateEvent {
/// A mapping of event type and state_key to some value `T`, usually an `EventId`. /// A mapping of event type and state_key to some value `T`, usually an `EventId`.
pub type StateMap<T> = BTreeMap<(EventType, String), T>; pub type StateMap<T> = BTreeMap<(EventType, String), T>;
/// A mapping of `EventId` to `T`, usually a `StateEvent`.
pub type EventMap<T> = BTreeMap<EventId, T>;
struct StateResolution { struct StateResolution {
// Should any information be kept or should all of it be fetched from the // For now the StateResolution struct is empty. If "caching `event_map` between `resolve` calls
// StateStore trait? // ends up being more efficient it may have an `event_map` field.
event_map: BTreeMap<EventId, StateEvent>,
// fields for temp storage during resolution??
/// The events that conflict and their auth chains.
conflicting_events: StateMap<Vec<EventId>>,
} }
impl StateResolution { impl StateResolution {
/// The point of this all. Resolve the conflicting set of . /// The point of this all, resolve the possibly conflicting sets of events.
fn resolve(&mut self, events: Vec<StateMap<EventId>>) -> StateMap<EventId> { } pub fn resolve(
&self,
room_id: &RoomId,
room_version: &RoomVersionId,
state_sets: &[StateMap<EventId>],
event_map: Option<EventMap<StateEvent>>,
store: &dyn StateStore,
) -> Result<ResolutionResult>;
} }
@ -34,11 +38,9 @@ trait StateStore {
/// Return a single event based on the EventId. /// Return a single event based on the EventId.
fn get_event(&self, event_id: &EventId) -> Result<StateEvent, String>; fn get_event(&self, event_id: &EventId) -> Result<StateEvent, String>;
/// Returns the events that correspond to the `event_ids` sorted in the same order. // There are 3 methods that have default implementations `get_events`, `auth_event_ids` and
fn get_events(&self, event_ids: &[EventId]) -> Result<Vec<StateEvent>, String>; // `auth_chain_diff`. Each could be overridden if the user has an optimization with their database of
// choice.
/// Returns a Vec of the related auth events to the given `event`.
fn auth_event_ids(&self, room_id: &RoomId, event_ids: &[EventId]) -> Result<Vec<EventId>, String>;
} }
``` ```

View File

@ -26,6 +26,8 @@ pub enum RedactAllowed {
No, 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( pub fn auth_types_for_event(
kind: EventType, kind: EventType,
sender: &UserId, sender: &UserId,
@ -71,6 +73,10 @@ pub fn auth_types_for_event(
auth_types 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( pub fn auth_check(
room_version: &RoomVersionId, room_version: &RoomVersionId,
event: &StateEvent, event: &StateEvent,
@ -107,7 +113,7 @@ pub fn auth_check(
// TODO do_size_check is false when called by `iterative_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, // 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 // Implementation of https://matrix.org/docs/spec/rooms/v1#authorization-rules
// //
@ -129,7 +135,7 @@ pub fn auth_check(
.get("room_version") .get("room_version")
.cloned() .cloned()
// synapse defaults to version 1 // synapse defaults to version 1
.unwrap_or(serde_json::json!("1")), .unwrap_or_else(|| serde_json::json!("1")),
) )
.is_err() .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<StateEvent>) -> Option<bool> { pub fn can_send_event(event: &StateEvent, auth_events: &StateMap<StateEvent>) -> Option<bool> {
let ple = auth_events.get(&(EventType::RoomPowerLevels, Some("".into()))); 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<StateEvent>, name: &str, default: i64) -> i64 { pub fn get_named_level(auth_events: &StateMap<StateEvent>, name: &str, default: i64) -> i64 {
let power_level_event = auth_events.get(&(EventType::RoomPowerLevels, Some("".into()))); let power_level_event = auth_events.get(&(EventType::RoomPowerLevels, Some("".into())));
if let Some(pl) = power_level_event { if let Some(pl) = power_level_event {
@ -675,6 +683,8 @@ pub fn get_named_level(auth_events: &StateMap<StateEvent>, 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<StateEvent>) -> i64 { pub fn get_user_power_level(user_id: &UserId, auth_events: &StateMap<StateEvent>) -> i64 {
if let Some(pl) = auth_events.get(&(EventType::RoomPowerLevels, Some("".into()))) { if let Some(pl) = auth_events.get(&(EventType::RoomPowerLevels, Some("".into()))) {
if let Ok(content) = pl.deserialize_content::<room::power_levels::PowerLevelsEventContent>() if let Ok(content) = pl.deserialize_content::<room::power_levels::PowerLevelsEventContent>()
@ -706,6 +716,8 @@ pub fn get_user_power_level(user_id: &UserId, auth_events: &StateMap<StateEvent>
} }
} }
/// 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( pub fn get_send_level(
e_type: EventType, e_type: EventType,
state_key: Option<String>, state_key: Option<String>,
@ -720,7 +732,7 @@ pub fn get_send_level(
.events .events
.get(&e_type) .get(&e_type)
.cloned() .cloned()
.unwrap_or(js_int::int!(50)) .unwrap_or_else(|| js_int::int!(50))
.into(); .into();
let state_def: i64 = content.state_default.into(); let state_def: i64 = content.state_default.into();
let event_def: i64 = content.events_default.into(); let event_def: i64 = content.events_default.into();

View File

@ -1,5 +1,3 @@
#![allow(clippy::or_fun_call)]
use std::{ use std::{
cmp::Reverse, cmp::Reverse,
collections::{BTreeMap, BTreeSet, BinaryHeap}, collections::{BTreeMap, BTreeSet, BinaryHeap},
@ -266,7 +264,7 @@ impl StateResolution {
"SEP {:?}", "SEP {:?}",
event_ids event_ids
.iter() .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::<Vec<_>>() .collect::<Vec<_>>()
); );
@ -560,7 +558,7 @@ impl StateResolution {
tracing::debug!("event to check {:?}", event.event_id().to_string()); tracing::debug!("event to check {:?}", event.event_id().to_string());
if event_auth::auth_check(room_version, &event, auth_events, false) 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)? .map_err(Error::TempString)?
{ {
// add event to resolved state map // add event to resolved state map
@ -713,7 +711,7 @@ impl StateResolution {
while !state.is_empty() { while !state.is_empty() {
// we just checked if it was empty so unwrap is fine // we just checked if it was empty so unwrap is fine
let eid = state.pop().unwrap(); 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 // prefer the store to event as the store filters dedups the events
// otherwise it seems we can loop forever // otherwise it seems we can loop forever
for aid in self for aid in self