1use std::{
2 borrow::Borrow,
3 collections::{BTreeMap, BTreeSet, HashSet},
4};
5
6use js_int::Int;
7use ruma_common::{
8 EventId, OwnedUserId, UserId, room::JoinRuleKind, room_version_rules::AuthorizationRules,
9};
10use ruma_events::{
11 StateEventType, TimelineEventType,
12 room::{member::MembershipState, power_levels::UserPowerLevel},
13};
14use serde_json::value::RawValue as RawJsonValue;
15use tracing::{debug, info, instrument, warn};
16
17mod room_member;
18#[cfg(test)]
19mod tests;
20
21use self::room_member::check_room_member;
22use crate::{
23 Event,
24 events::{
25 RoomCreateEvent, RoomJoinRulesEvent, RoomMemberEvent, RoomPowerLevelsEvent,
26 RoomThirdPartyInviteEvent,
27 member::{RoomMemberEventContent, RoomMemberEventOptionExt},
28 power_levels::{RoomPowerLevelsEventOptionExt, RoomPowerLevelsIntField},
29 },
30 utils::RoomIdExt,
31};
32
33pub fn auth_types_for_event(
44 event_type: &TimelineEventType,
45 sender: &UserId,
46 state_key: Option<&str>,
47 content: &RawJsonValue,
48 rules: &AuthorizationRules,
49) -> Result<Vec<(StateEventType, String)>, String> {
50 if event_type == &TimelineEventType::RoomCreate {
52 return Ok(vec![]);
53 }
54
55 let mut auth_types = vec![
60 (StateEventType::RoomPowerLevels, "".to_owned()),
61 (StateEventType::RoomMember, sender.to_string()),
62 ];
63
64 if !rules.room_create_event_id_as_room_id {
66 auth_types.push((StateEventType::RoomCreate, "".to_owned()));
67 }
68
69 if event_type == &TimelineEventType::RoomMember {
71 let Some(state_key) = state_key else {
73 return Err("missing `state_key` field for `m.room.member` event".to_owned());
74 };
75 let key = (StateEventType::RoomMember, state_key.to_owned());
76 if !auth_types.contains(&key) {
77 auth_types.push(key);
78 }
79
80 let content = RoomMemberEventContent::new(content);
81 let membership = content.membership()?;
82
83 if matches!(
86 membership,
87 MembershipState::Join | MembershipState::Invite | MembershipState::Knock
88 ) {
89 let key = (StateEventType::RoomJoinRules, "".to_owned());
90 if !auth_types.contains(&key) {
91 auth_types.push(key);
92 }
93 }
94
95 if membership == MembershipState::Invite {
99 let third_party_invite = content.third_party_invite()?;
100
101 if let Some(third_party_invite) = third_party_invite {
102 let token = third_party_invite.token()?.to_owned();
103 let key = (StateEventType::RoomThirdPartyInvite, token);
104 if !auth_types.contains(&key) {
105 auth_types.push(key);
106 }
107 }
108 }
109
110 if membership == MembershipState::Join && rules.restricted_join_rule {
116 let join_authorised_via_users_server = content.join_authorised_via_users_server()?;
117 if let Some(user_id) = join_authorised_via_users_server {
118 let key = (StateEventType::RoomMember, user_id.to_string());
119 if !auth_types.contains(&key) {
120 auth_types.push(key);
121 }
122 }
123 }
124 }
125
126 Ok(auth_types)
127}
128
129#[instrument(skip_all, fields(event_id = incoming_event.event_id().borrow().as_str()))]
144pub fn check_state_independent_auth_rules<E: Event>(
145 rules: &AuthorizationRules,
146 incoming_event: impl Event,
147 fetch_event: impl Fn(&EventId) -> Option<E>,
148) -> Result<(), String> {
149 debug!("starting state-independent auth check");
150
151 if *incoming_event.event_type() == TimelineEventType::RoomCreate {
153 let room_create_event = RoomCreateEvent::new(incoming_event);
154
155 return check_room_create(room_create_event, rules);
156 }
157
158 let expected_auth_types = auth_types_for_event(
159 incoming_event.event_type(),
160 incoming_event.sender(),
161 incoming_event.state_key(),
162 incoming_event.content(),
163 rules,
164 )?
165 .into_iter()
166 .map(|(event_type, state_key)| (TimelineEventType::from(event_type), state_key))
167 .collect::<HashSet<_>>();
168
169 let Some(room_id) = incoming_event.room_id() else {
170 return Err("missing `room_id` field for event".to_owned());
171 };
172
173 let mut seen_auth_types: HashSet<(TimelineEventType, String)> =
174 HashSet::with_capacity(expected_auth_types.len());
175
176 for auth_event_id in incoming_event.auth_events() {
178 let event_id = auth_event_id.borrow();
179
180 let Some(auth_event) = fetch_event(event_id) else {
181 return Err(format!("failed to find auth event {event_id}"));
182 };
183
184 if auth_event.room_id().is_none_or(|auth_room_id| auth_room_id != room_id) {
186 return Err(format!("auth event {event_id} not in the same room"));
187 }
188
189 let event_type = auth_event.event_type();
190 let state_key = auth_event
191 .state_key()
192 .ok_or_else(|| format!("auth event {event_id} has no `state_key`"))?;
193 let key = (event_type.clone(), state_key.to_owned());
194
195 if seen_auth_types.contains(&key) {
197 return Err(format!(
198 "duplicate auth event {event_id} for ({event_type}, {state_key}) pair"
199 ));
200 }
201
202 if !expected_auth_types.contains(&key) {
205 return Err(format!(
206 "unexpected auth event {event_id} with ({event_type}, {state_key}) pair"
207 ));
208 }
209
210 if auth_event.rejected() {
213 return Err(format!("rejected auth event {event_id}"));
214 }
215
216 seen_auth_types.insert(key);
217 }
218
219 if !rules.room_create_event_id_as_room_id
221 && !seen_auth_types
222 .iter()
223 .any(|(event_type, _)| *event_type == TimelineEventType::RoomCreate)
224 {
225 return Err("no `m.room.create` event in auth events".to_owned());
226 }
227
228 if rules.room_create_event_id_as_room_id {
230 let room_create_event_id = room_id.room_create_event_id().map_err(|error| {
231 format!("could not construct `m.room.create` event ID from room ID: {error}")
232 })?;
233
234 let room_create_event = fetch_event(&room_create_event_id).ok_or_else(|| {
235 format!("failed to find `m.room.create` event {room_create_event_id}")
236 })?;
237
238 if room_create_event.rejected() {
239 return Err(format!("rejected `m.room.create` event {room_create_event_id}"));
240 }
241 }
242
243 Ok(())
244}
245
246#[instrument(skip_all, fields(event_id = incoming_event.event_id().borrow().as_str()))]
268pub fn check_state_dependent_auth_rules<E: Event>(
269 rules: &AuthorizationRules,
270 incoming_event: impl Event,
271 fetch_state: impl Fn(&StateEventType, &str) -> Option<E>,
272) -> Result<(), String> {
273 debug!("starting state-dependent auth check");
274
275 if *incoming_event.event_type() == TimelineEventType::RoomCreate {
277 debug!("allowing `m.room.create` event");
278 return Ok(());
279 }
280
281 let room_create_event = fetch_state.room_create_event()?;
282
283 let federate = room_create_event.federate()?;
286 if !federate
287 && room_create_event.sender().server_name() != incoming_event.sender().server_name()
288 {
289 return Err("\
290 room is not federated and event's sender domain \
291 does not match `m.room.create` event's sender domain"
292 .to_owned());
293 }
294
295 let sender = incoming_event.sender();
296
297 if rules.special_case_room_aliases && *incoming_event.event_type() == "m.room.aliases".into() {
299 debug!("starting m.room.aliases check");
300 if incoming_event.state_key() != Some(sender.server_name().as_str()) {
304 return Err("\
305 server name of the `state_key` of `m.room.aliases` event \
306 does not match the server name of the sender"
307 .to_owned());
308 }
309
310 info!("`m.room.aliases` event was allowed");
312 return Ok(());
313 }
314
315 if *incoming_event.event_type() == TimelineEventType::RoomMember {
317 let room_member_event = RoomMemberEvent::new(incoming_event);
318 return check_room_member(room_member_event, rules, room_create_event, fetch_state);
319 }
320
321 let sender_membership = fetch_state.user_membership(sender)?;
323 tracing::info!(?sender_membership);
324
325 if sender_membership != MembershipState::Join {
326 return Err("sender's membership is not `join`".to_owned());
327 }
328
329 let creators = room_create_event.creators(rules)?;
330 let current_room_power_levels_event = fetch_state.room_power_levels_event();
331
332 let sender_power_level =
333 current_room_power_levels_event.user_power_level(sender, &creators, rules)?;
334
335 if *incoming_event.event_type() == TimelineEventType::RoomThirdPartyInvite {
337 let invite_power_level = current_room_power_levels_event
340 .get_as_int_or_default(RoomPowerLevelsIntField::Invite, rules)?;
341
342 if sender_power_level < invite_power_level {
343 return Err("sender does not have enough power to send invites in this room".to_owned());
344 }
345
346 info!("`m.room.third_party_invite` event was allowed");
347 return Ok(());
348 }
349
350 let event_type_power_level = current_room_power_levels_event.event_power_level(
353 incoming_event.event_type(),
354 incoming_event.state_key(),
355 rules,
356 )?;
357 if sender_power_level < event_type_power_level {
358 return Err(format!(
359 "sender does not have enough power to send event of type `{}`",
360 incoming_event.event_type()
361 ));
362 }
363
364 if incoming_event.state_key().is_some_and(|k| k.starts_with('@'))
367 && incoming_event.state_key() != Some(incoming_event.sender().as_str())
368 {
369 return Err(
370 "sender cannot send event with `state_key` matching another user's ID".to_owned()
371 );
372 }
373
374 if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
376 let room_power_levels_event = RoomPowerLevelsEvent::new(incoming_event);
377 return check_room_power_levels(
378 room_power_levels_event,
379 current_room_power_levels_event,
380 rules,
381 sender_power_level,
382 &creators,
383 );
384 }
385
386 if rules.special_case_room_redaction
388 && *incoming_event.event_type() == TimelineEventType::RoomRedaction
389 {
390 return check_room_redaction(
391 incoming_event,
392 current_room_power_levels_event,
393 rules,
394 sender_power_level,
395 );
396 }
397
398 info!("allowing event passed all checks");
400 Ok(())
401}
402
403fn check_room_create(
405 room_create_event: RoomCreateEvent<impl Event>,
406 rules: &AuthorizationRules,
407) -> Result<(), String> {
408 debug!("start `m.room.create` check");
409
410 if room_create_event.prev_events().next().is_some() {
412 return Err("`m.room.create` event cannot have previous events".into());
413 }
414
415 if rules.room_create_event_id_as_room_id {
416 if room_create_event.room_id().is_some() {
418 return Err("`m.room.create` event cannot have a `room_id` field".into());
419 }
420 } else {
421 let Some(room_id) = room_create_event.room_id() else {
423 return Err("missing `room_id` field in `m.room.create` event".into());
424 };
425 let Some(room_id_server_name) = room_id.server_name() else {
426 return Err(
427 "invalid `room_id` field in `m.room.create` event: could not parse server name"
428 .into(),
429 );
430 };
431
432 if room_id_server_name != room_create_event.sender().server_name() {
433 return Err("invalid `room_id` field in `m.room.create` event: server name does not match sender's server name".into());
434 }
435 }
436
437 if !rules.use_room_create_sender && !room_create_event.has_creator()? {
444 return Err("missing `creator` field in `m.room.create` event".into());
445 }
446
447 room_create_event.additional_creators(rules)?;
450
451 info!("`m.room.create` event was allowed");
453 Ok(())
454}
455
456fn check_room_power_levels(
458 room_power_levels_event: RoomPowerLevelsEvent<impl Event>,
459 current_room_power_levels_event: Option<RoomPowerLevelsEvent<impl Event>>,
460 rules: &AuthorizationRules,
461 sender_power_level: UserPowerLevel,
462 room_creators: &HashSet<OwnedUserId>,
463) -> Result<(), String> {
464 debug!("starting m.room.power_levels check");
465
466 let new_int_fields = room_power_levels_event.int_fields_map(rules)?;
469
470 let new_events = room_power_levels_event.events(rules)?;
473 let new_notifications = room_power_levels_event.notifications(rules)?;
474
475 let new_users = room_power_levels_event.users(rules)?;
480
481 if rules.explicitly_privilege_room_creators
484 && new_users.is_some_and(|new_users| {
485 room_creators.iter().any(|creator| new_users.contains_key(creator))
486 })
487 {
488 return Err("creator user IDs are not allowed in the `users` field".to_owned());
489 }
490
491 debug!("validation of power event finished");
492
493 let Some(current_room_power_levels_event) = current_room_power_levels_event else {
495 info!("initial m.room.power_levels event allowed");
496 return Ok(());
497 };
498
499 for field in RoomPowerLevelsIntField::ALL {
502 let current_power_level = current_room_power_levels_event.get_as_int(*field, rules)?;
503 let new_power_level = new_int_fields.get(field).copied();
504
505 if current_power_level == new_power_level {
506 continue;
507 }
508
509 let current_power_level_too_big =
512 current_power_level.unwrap_or_else(|| field.default_value()) > sender_power_level;
513 let new_power_level_too_big =
515 new_power_level.unwrap_or_else(|| field.default_value()) > sender_power_level;
516
517 if current_power_level_too_big || new_power_level_too_big {
518 return Err(format!(
519 "sender does not have enough power to change the power level of `{field}`"
520 ));
521 }
522 }
523
524 let current_events = current_room_power_levels_event.events(rules)?;
527 check_power_level_maps(
528 current_events.as_ref(),
529 new_events.as_ref(),
530 &sender_power_level,
531 |_, current_power_level| {
532 current_power_level > sender_power_level
536 },
537 |ev_type| {
538 format!(
539 "sender does not have enough power to change the `{ev_type}` event type power level"
540 )
541 },
542 )?;
543
544 if rules.limit_notifications_power_levels {
547 let current_notifications = current_room_power_levels_event.notifications(rules)?;
548 check_power_level_maps(
549 current_notifications.as_ref(),
550 new_notifications.as_ref(),
551 &sender_power_level,
552 |_, current_power_level| {
553 current_power_level > sender_power_level
558 },
559 |key| {
560 format!(
561 "sender does not have enough power to change the `{key}` notification power level"
562 )
563 },
564 )?;
565 }
566
567 let current_users = current_room_power_levels_event.users(rules)?;
570 check_power_level_maps(
571 current_users,
572 new_users,
573 &sender_power_level,
574 |user_id, current_power_level| {
575 user_id != room_power_levels_event.sender() && current_power_level >= sender_power_level
580 },
581 |user_id| format!("sender does not have enough power to change `{user_id}`'s power level"),
582 )?;
583
584 info!("m.room.power_levels event allowed");
586 Ok(())
587}
588
589fn check_power_level_maps<K: Ord>(
607 current: Option<&BTreeMap<K, Int>>,
608 new: Option<&BTreeMap<K, Int>>,
609 sender_power_level: &UserPowerLevel,
610 reject_current_power_level_change_fn: impl FnOnce(&K, Int) -> bool + Copy,
611 error_fn: impl FnOnce(&K) -> String,
612) -> Result<(), String> {
613 let keys_to_check = current
614 .iter()
615 .flat_map(|m| m.keys())
616 .chain(new.iter().flat_map(|m| m.keys()))
617 .collect::<BTreeSet<_>>();
618
619 for key in keys_to_check {
620 let current_power_level = current.as_ref().and_then(|m| m.get(key));
621 let new_power_level = new.as_ref().and_then(|m| m.get(key));
622
623 if current_power_level == new_power_level {
624 continue;
625 }
626
627 let current_power_level_change_rejected = current_power_level
629 .is_some_and(|power_level| reject_current_power_level_change_fn(key, *power_level));
630
631 let new_power_level_too_big = new_power_level.is_some_and(|pl| pl > sender_power_level);
634
635 if current_power_level_change_rejected || new_power_level_too_big {
636 return Err(error_fn(key));
637 }
638 }
639
640 Ok(())
641}
642
643fn check_room_redaction(
645 room_redaction_event: impl Event,
646 current_room_power_levels_event: Option<RoomPowerLevelsEvent<impl Event>>,
647 rules: &AuthorizationRules,
648 sender_level: UserPowerLevel,
649) -> Result<(), String> {
650 let redact_level = current_room_power_levels_event
651 .get_as_int_or_default(RoomPowerLevelsIntField::Redact, rules)?;
652
653 if sender_level >= redact_level {
655 info!("`m.room.redaction` event allowed via power levels");
656 return Ok(());
657 }
658
659 if room_redaction_event.event_id().borrow().server_name()
662 == room_redaction_event.redacts().as_ref().and_then(|&id| id.borrow().server_name())
663 {
664 info!("`m.room.redaction` event allowed via room version 1 rules");
665 return Ok(());
666 }
667
668 Err("`m.room.redaction` event did not pass any of the allow rules".to_owned())
670}
671
672trait FetchStateExt<E: Event> {
673 fn room_create_event(&self) -> Result<RoomCreateEvent<E>, String>;
674
675 fn user_membership(&self, user_id: &UserId) -> Result<MembershipState, String>;
676
677 fn room_power_levels_event(&self) -> Option<RoomPowerLevelsEvent<E>>;
678
679 fn join_rule(&self) -> Result<JoinRuleKind, String>;
680
681 fn room_third_party_invite_event(&self, token: &str) -> Option<RoomThirdPartyInviteEvent<E>>;
682}
683
684impl<E, F> FetchStateExt<E> for F
685where
686 F: Fn(&StateEventType, &str) -> Option<E>,
687 E: Event,
688{
689 fn room_create_event(&self) -> Result<RoomCreateEvent<E>, String> {
690 self(&StateEventType::RoomCreate, "")
691 .map(RoomCreateEvent::new)
692 .ok_or_else(|| "no `m.room.create` event in current state".to_owned())
693 }
694
695 fn user_membership(&self, user_id: &UserId) -> Result<MembershipState, String> {
696 self(&StateEventType::RoomMember, user_id.as_str()).map(RoomMemberEvent::new).membership()
697 }
698
699 fn room_power_levels_event(&self) -> Option<RoomPowerLevelsEvent<E>> {
700 self(&StateEventType::RoomPowerLevels, "").map(RoomPowerLevelsEvent::new)
701 }
702
703 fn join_rule(&self) -> Result<JoinRuleKind, String> {
704 self(&StateEventType::RoomJoinRules, "")
705 .map(RoomJoinRulesEvent::new)
706 .ok_or_else(|| "no `m.room.join_rules` event in current state".to_owned())?
707 .join_rule()
708 }
709
710 fn room_third_party_invite_event(&self, token: &str) -> Option<RoomThirdPartyInviteEvent<E>> {
711 self(&StateEventType::RoomThirdPartyInvite, token).map(RoomThirdPartyInviteEvent::new)
712 }
713}