diff --git a/src/event_auth.rs b/src/event_auth.rs index ecb1f29a..9459d351 100644 --- a/src/event_auth.rs +++ b/src/event_auth.rs @@ -180,7 +180,14 @@ pub fn auth_check( } if event.kind() == EventType::RoomPowerLevels { - if !check_power_levels(room_version, event, &auth_events)? { + tracing::info!("starting m.room.power_levels check"); + if let Some(required_pwr_lvl) = check_power_levels(room_version, event, &auth_events) { + if !required_pwr_lvl { + tracing::warn!("power level was not allowed"); + return Some(false); + } + } else { + tracing::warn!("power level was not allowed"); return Some(false); } tracing::info!("power levels event allowed"); @@ -372,6 +379,13 @@ fn can_send_event(event: &StateEvent, auth_events: &StateMap) -> Opt let send_level = get_send_level(event.kind(), event.state_key(), ple); let user_level = get_user_power_level(event.sender(), auth_events); + tracing::debug!( + "{} snd {} usr {}", + event.event_id().unwrap().to_string(), + send_level, + user_level + ); + if user_level < send_level { return Some(false); } @@ -394,16 +408,17 @@ fn check_power_levels( ) -> Option { use itertools::Itertools; - let key = (power_event.kind(), power_event.state_key()?); + let key = (power_event.kind(), power_event.state_key().unwrap()); let current_state = auth_events.get(&key)?; let user_content = power_event .deserialize_content::() - .ok()?; + .unwrap(); let current_content = current_state .deserialize_content::() - .ok()?; + .unwrap(); + tracing::info!("validation of power event finished"); // validation of users is done in Ruma, synapse for loops validating user_ids and integers here let user_level = get_user_power_level(power_event.sender(), auth_events); @@ -424,6 +439,8 @@ fn check_power_levels( event_levels_to_check.push(ev_id); } + tracing::debug!("events to check {:?}", event_levels_to_check); + // TODO validate MSC2209 depending on room version check "notifications". // synapse does this very differently with the loops (see comments below) // but since we have a validated JSON event we can check the levels directly @@ -435,6 +452,7 @@ fn check_power_levels( let old_level_too_big = old_level > user_level; let new_level_too_big = new_level > user_level; if old_level_too_big || new_level_too_big { + tracing::info!("m.room.power_level cannot add ops > than own"); return Some(false); // cannot add ops greater than own } } @@ -456,6 +474,7 @@ fn check_power_levels( } if user != power_event.sender() { if old_level.map(|int| (*int).into()) == Some(user_level) { + tracing::info!("m.room.power_level cannot remove ops == to own"); return Some(false); // cannot remove ops level == to own } } @@ -463,6 +482,7 @@ fn check_power_levels( let old_level_too_big = old_level.map(|int| (*int).into()) > Some(user_level); let new_level_too_big = new_level.map(|int| (*int).into()) > Some(user_level); if old_level_too_big || new_level_too_big { + tracing::info!("m.room.power_level failed to add ops > than own"); return Some(false); // cannot add ops greater than own } } @@ -480,6 +500,7 @@ fn check_power_levels( let old_level_too_big = old_level.map(|int| (*int).into()) > Some(user_level); let new_level_too_big = new_level.map(|int| (*int).into()) > Some(user_level); if old_level_too_big || new_level_too_big { + tracing::info!("m.room.power_level failed to add ops > than own"); return Some(false); // cannot add ops greater than own } } @@ -579,21 +600,30 @@ fn get_send_level( state_key: Option, power_lvl: Option<&StateEvent>, ) -> i64 { + tracing::debug!("{:?} {:?}", e_type, state_key); if let Some(ple) = power_lvl { - if state_key.is_some() { - if let Ok(content) = - serde_json::from_value::(ple.content()) - { - if let Some(_specific_ev) = content.events.get(&e_type) { - // this is done differently in synapse the `specific_ev` is set and if `users_default` is - // found it is used + if let Ok(content) = + serde_json::from_value::(ple.content()) + { + let mut lvl: i64 = content + .events + .get(&e_type) + .cloned() + .unwrap_or(js_int::int!(50)) + .into(); + let state_def: i64 = content.state_default.into(); + let event_def: i64 = content.events_default.into(); + if state_key.is_some() { + if state_def > lvl { + lvl = event_def; } - content.users_default.into() - } else { - return 50; } + if event_def > lvl { + lvl = event_def; + } + return lvl; } else { - return 0; + return 50; } } else { return 0; diff --git a/src/lib.rs b/src/lib.rs index d0f18cc0..c524b051 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,7 +140,13 @@ impl StateResolution { // TODO make sure each conflicting event is in event_map?? // synapse says `full_set = {eid for eid in full_conflicted_set if eid in event_map}` all_conflicted.retain(|id| event_map.contains_key(id)); - + println!( + "ALL {:?}", + all_conflicted + .iter() + .map(ToString::to_string) + .collect::>() + ); // get only the power events with a state_key: "" or ban/kick event (sender != state_key) let power_events = all_conflicted .iter() @@ -148,7 +154,7 @@ impl StateResolution { .cloned() .collect::>(); - tracing::debug!( + println!( "POWER {:?}", power_events .iter() @@ -165,7 +171,7 @@ impl StateResolution { &all_conflicted, ); - tracing::debug!( + println!( "SRTD {:?}", sorted_power_levels .iter() @@ -203,7 +209,7 @@ impl StateResolution { .cloned() .collect::>(); - tracing::debug!( + println!( "LEFT {:?}", events_to_resolve .iter() @@ -218,7 +224,7 @@ impl StateResolution { let sorted_left_events = self.mainline_sort(room_id, &events_to_resolve, power_event, &event_map, store); - tracing::debug!( + println!( "SORTED LEFT {:?}", sorted_left_events .iter() @@ -324,7 +330,7 @@ impl StateResolution { let mut event_to_pl = BTreeMap::new(); for (idx, event_id) in graph.keys().enumerate() { let pl = self.get_power_level_for_sender(room_id, &event_id, event_map, store); - tracing::debug!("{} power level {}", event_id.to_string(), pl); + println!("{} power level {}", event_id.to_string(), pl); event_to_pl.insert(event_id.clone(), pl); @@ -424,16 +430,17 @@ impl StateResolution { &self, room_id: &RoomId, event_id: &EventId, - _event_map: &EventMap, // TODO use event_map over store ?? + event_map: &EventMap, // TODO use event_map over store ?? store: &dyn StateStore, ) -> i64 { tracing::info!("fetch event senders ({}) power level", event_id.to_string()); - + let event = event_map.get(event_id); let mut pl = None; // TODO store.auth_event_ids returns "self" with the event ids is this ok // event.auth_event_ids does not include its own event id ? - for aid in store.auth_event_ids(room_id, &[event_id.clone()]).unwrap() { - if let Ok(aev) = store.get_event(&aid) { + for aid in event_map.get(event_id).unwrap().auth_event_ids() { + println!("aid {}", aid.to_string()); + if let Some(aev) = event_map.get(&aid) { if aev.is_type_and_key(EventType::RoomPowerLevels, "") { pl = Some(aev); break; @@ -442,8 +449,10 @@ impl StateResolution { } if pl.is_none() { - for aid in store.auth_event_ids(room_id, &[event_id.clone()]).unwrap() { - if let Ok(aev) = store.get_event(&aid) { + for aid in event_map.get(event_id).unwrap().auth_event_ids() { + println!("aid NONE {}", aid.to_string()); + + if let Some(aev) = event_map.get(&aid) { if aev.is_type_and_key(EventType::RoomCreate, "") { if let Ok(content) = aev .deserialize_content::() @@ -467,7 +476,12 @@ impl StateResolution { }) .flatten() { - content.users_default.into() + if let Some(ev) = event { + if let Some(user) = content.users.get(ev.sender()) { + return (*user).into(); + } + } + content.state_default.into() } else { 0 } @@ -510,6 +524,7 @@ impl StateResolution { } } + tracing::debug!("event to check {:?}", event.event_id().unwrap().to_string()); if !event_auth::auth_check(room_version, &event, auth_events) .ok_or("Auth check failed due to deserialization most likely".to_string()) .unwrap() diff --git a/src/state_event.rs b/src/state_event.rs index d24f5e3b..1a2d0e78 100644 --- a/src/state_event.rs +++ b/src/state_event.rs @@ -35,8 +35,8 @@ impl StateEvent { .contains(&content.membership) { return event.sender.as_str() - // TODO does None here mean the same as state_key = "" - != event.state_key.as_deref().unwrap_or(""); + // TODO is None here a failure + != event.state_key.as_deref().unwrap_or("NOT A STATE KEY"); } } @@ -44,32 +44,9 @@ impl StateEvent { } _ => false, }, - Pdu::RoomV3Pdu(event) => event.state_key == Some("".into()), - }, - Self::Sync(any_event) => match any_event { - PduStub::RoomV1PduStub(event) => match event.kind { - EventType::RoomPowerLevels - | EventType::RoomJoinRules - | EventType::RoomCreate => event.state_key == Some("".into()), - EventType::RoomMember => { - if let Ok(content) = - serde_json::from_value::(event.content.clone()) - { - if [MembershipState::Leave, MembershipState::Ban] - .contains(&content.membership) - { - return event.sender.as_str() - // TODO does None here mean the same as state_key = "" - != event.state_key.as_deref().unwrap_or(""); - } - } - - false - } - _ => false, - }, - PduStub::RoomV3PduStub(event) => event.state_key == Some("".into()), + Pdu::RoomV3Pdu(event) => panic!(), }, + Self::Sync(any_event) => panic!(), } } pub fn deserialize_content( diff --git a/tests/state_res.rs b/tests/state_res.rs index b3b8fb64..cdfd656d 100644 --- a/tests/state_res.rs +++ b/tests/state_res.rs @@ -286,10 +286,10 @@ fn INITIAL_EDGES() -> Vec { fn do_check(events: &[StateEvent], edges: Vec>, expected_state_ids: Vec) { use itertools::Itertools; - // to activate logging use `RUST_LOG=debug cargo t` - tracer::fmt() - .with_env_filter(tracer::EnvFilter::from_default_env()) - .init(); + // to activate logging use `RUST_LOG=debug cargo t one_test_only` + // tracer::fmt() + // .with_env_filter(tracer::EnvFilter::from_default_env()) + // .init(); let mut resolver = StateResolution::default(); // TODO what do we fill this with, everything ?? @@ -519,6 +519,59 @@ fn ban_vs_power_level() { } // #[test] +fn topic_basic() { + let events = &[ + to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event( + "PA1", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({"users": {alice(): 100, bobo(): 50}}), + ), + to_init_pdu_event("T2", alice(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event( + "PA2", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({"users": {alice(): 100, bobo(): 0}}), + ), + to_init_pdu_event( + "PAB", + bobo(), + EventType::RoomPowerLevels, + Some(""), + json!({"users": {alice(): 100, bobo(): 50}}), + ), + to_init_pdu_event("T3", bobo(), EventType::RoomTopic, Some(""), json!({})), + ]; + + let edges = vec![ + vec!["END", "PA2", "T2", "PA1", "T1", "START"], + vec!["END", "T3", "PB", "PA1"], + ] + .into_iter() + .map(|list| { + list.into_iter() + .map(|s| format!("${}:foo", s)) + .map(EventId::try_from) + .collect::, _>>() + .unwrap() + }) + .collect::>(); + + let expected_state_ids = vec!["PA2", "T2"] + .into_iter() + .map(|s| format!("${}:foo", s)) + .map(EventId::try_from) + .collect::, _>>() + .unwrap(); + + do_check(events, edges, expected_state_ids) +} + +#[test] fn topic_reset() { let events = &[ to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})),