1mod auth_types;
2mod room_member;
3#[cfg(test)]
4mod tests;
5
6use futures::{
7 TryStreamExt,
8 future::{join3, try_join},
9};
10use ruma::{
11 EventId, Int, OwnedUserId,
12 api::error::ErrorKind::InvalidParam,
13 events::{
14 StateEventType, TimelineEventType,
15 room::{member::MembershipState, power_levels::UserPowerLevel},
16 },
17 room_version_rules::{AuthorizationRules, RoomVersionRules},
18};
19use tuwunel_core::{
20 Err, Error, Result, err,
21 matrix::{Event, PduEvent},
22 trace,
23 utils::stream::{IterStream, TryReadyExt},
24};
25
26pub use self::auth_types::{AuthTypes, auth_types_for_event};
27use self::room_member::check_room_member;
28#[cfg(test)]
29use super::test_utils;
30use super::{
31 FetchEvent, FetchState, TypeStateKey, events,
32 events::{
33 RoomCreateEvent, RoomMemberEvent, RoomPowerLevelsEvent,
34 power_levels::{self, RoomPowerLevelsEventOptionExt, RoomPowerLevelsIntField},
35 },
36};
37
38#[must_use = "authorization outcomes must be handled"]
43pub enum AuthCheckOutcome {
44 Allow,
48
49 Deny(Error),
53}
54
55impl AuthCheckOutcome {
56 pub fn into_result(self) -> Result {
61 match self {
62 | Self::Allow => Ok(()),
63 | Self::Deny(error @ Error::Request(InvalidParam, ..)) => Err(error),
64 | Self::Deny(error) => Err(Error::AuthCheck(Box::new(error))),
65 }
66 }
67}
68
69#[tracing::instrument(
74 level = "debug",
75 skip_all,
76 fields(
77 event_id = ?incoming_event.event_id(),
78 )
79)]
80pub async fn auth_check<Fetch, State>(
81 rules: &RoomVersionRules,
82 incoming_event: &PduEvent,
83 fetch_event: Fetch,
84 fetch_state: State,
85) -> Result<AuthCheckOutcome>
86where
87 Fetch: FetchEvent,
88 State: FetchState,
89{
90 auth_check_outcome(rules, incoming_event, fetch_event, fetch_state).await
91}
92
93async fn auth_check_outcome<Fetch, State>(
94 rules: &RoomVersionRules,
95 incoming_event: &PduEvent,
96 fetch_event: Fetch,
97 fetch_state: State,
98) -> Result<AuthCheckOutcome>
99where
100 Fetch: FetchEvent,
101 State: FetchState,
102{
103 let dependent = check_state_dependent_auth_rules(rules, incoming_event, fetch_state);
104 let independent = check_state_independent_auth_rules(rules, incoming_event, fetch_event);
105
106 match try_join(independent, dependent).await {
107 | Ok(_) => Ok(AuthCheckOutcome::Allow),
108 | Err(error) => classify_auth_error(error),
109 }
110}
111
112pub(super) fn classify_auth_error(error: Error) -> Result<AuthCheckOutcome> {
113 match error {
114 | error @ (Error::Err(..) | Error::Request(InvalidParam, ..)) =>
115 Ok(AuthCheckOutcome::Deny(error)),
116 | error => Err(error),
117 }
118}
119
120#[tracing::instrument(
136 name = "independent",
137 level = "debug",
138 skip_all,
139 fields(
140 sender = ?incoming_event.sender(),
141 )
142)]
143pub(super) async fn check_state_independent_auth_rules<Fetch>(
144 rules: &RoomVersionRules,
145 incoming_event: &PduEvent,
146 fetch_event: Fetch,
147) -> Result
148where
149 Fetch: FetchEvent,
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.authorization);
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.authorization,
164 false,
165 )?;
166
167 let seen_auth_types = Vec::with_capacity(expected_auth_types.len());
169 let seen_auth_types = incoming_event
170 .auth_events()
171 .try_stream()
172 .and_then(async |event_id: &EventId| match fetch_event.get(event_id).await {
173 | Ok(auth_event) => Ok(auth_event),
174 | Err(e) if e.is_not_found() => Err!(Request(NotFound("auth event {event_id}: {e}"))),
175 | Err(e) => Err(auth_input_error(e)),
176 })
177 .ready_try_fold(seen_auth_types, |mut seen_auth_types, auth_event: PduEvent| {
178 let event_id = auth_event.event_id();
179
180 if auth_event.room_id() != incoming_event.room_id() {
182 return Err!("auth event {event_id} not in the same room");
183 }
184
185 let state_key = auth_event
186 .state_key()
187 .ok_or_else(|| err!("auth event {event_id} has no `state_key`"))?;
188
189 let event_type = auth_event.event_type();
190 let key: TypeStateKey = (event_type.to_cow_str().into(), state_key.into());
191
192 if seen_auth_types.contains(&key) {
195 return Err!(
196 "duplicate auth event {event_id} for ({event_type}, {state_key}) pair"
197 );
198 }
199
200 if !expected_auth_types.contains(&key) {
204 return Err!(
205 "unexpected auth event {event_id} with ({event_type}, {state_key}) pair"
206 );
207 }
208
209 if auth_event.rejected() {
212 return Err!("rejected auth event {event_id}");
213 }
214
215 seen_auth_types.push(key);
216 Ok(seen_auth_types)
217 })
218 .await?;
219
220 if !rules
222 .authorization
223 .room_create_event_id_as_room_id
224 && !seen_auth_types
225 .iter()
226 .any(|(event_type, _)| *event_type == StateEventType::RoomCreate)
227 {
228 return Err!("no `m.room.create` event in auth events");
229 }
230
231 if rules
234 .authorization
235 .room_create_event_id_as_room_id
236 {
237 let room_create_event_id = incoming_event
238 .room_id()
239 .as_event_id()
240 .map_err(|e| {
241 err!(Request(InvalidParam(
242 "could not construct `m.room.create` event ID from room ID: {e}"
243 )))
244 })?;
245
246 let room_create_event = fetch_event
247 .get::<PduEvent>(&room_create_event_id)
248 .await
249 .map_err(|error| match error {
250 | error if error.is_not_found() => err!(Request(NotFound(
251 "failed to find `m.room.create` event {room_create_event_id}"
252 ))),
253 | error => auth_input_error(error),
254 })?;
255
256 if room_create_event.rejected() {
257 return Err!("rejected `m.room.create` event {room_create_event_id}");
258 }
259 }
260
261 Ok(())
262}
263
264#[tracing::instrument(
289 name = "dependent",
290 level = "debug",
291 skip_all,
292 fields(
293 sender = ?incoming_event.sender(),
294 )
295)]
296pub(super) async fn check_state_dependent_auth_rules<Fetch>(
297 rules: &RoomVersionRules,
298 incoming_event: &PduEvent,
299 fetch_state: Fetch,
300) -> Result
301where
302 Fetch: FetchState,
303{
304 if *incoming_event.event_type() == TimelineEventType::RoomCreate {
306 trace!("allowing `m.room.create` event");
307 return Ok(());
308 }
309
310 let sender = incoming_event.sender();
311 let (room_create_event, sender_membership, current_room_power_levels_event) = join3(
312 fetch_state.room_create_event(),
313 fetch_state.user_membership(sender),
314 fetch_state.room_power_levels_event(),
315 )
316 .await;
317
318 let room_create_event = room_create_event?;
322 let federate = room_create_event
323 .federate()
324 .map_err(auth_input_error)?;
325
326 if !federate
327 && room_create_event.sender().server_name() != incoming_event.sender().server_name()
328 {
329 return Err!(
330 "room is not federated and event's sender domain does not match `m.room.create` \
331 event's sender domain"
332 );
333 }
334
335 if rules.authorization.special_case_room_aliases
337 && incoming_event.event_type().to_cow_str() == "m.room.aliases"
338 {
339 trace!("starting m.room.aliases check");
340 if incoming_event.state_key() != Some(sender.server_name().as_str()) {
344 return Err!(
345 "server name of the `state_key` of `m.room.aliases` event does not match the \
346 server name of the sender"
347 );
348 }
349
350 trace!("`m.room.aliases` event was allowed");
352 return Ok(());
353 }
354
355 if *incoming_event.event_type() == TimelineEventType::RoomMember {
357 let room_member_event = RoomMemberEvent::new(incoming_event);
358
359 return check_room_member(
360 &room_member_event,
361 &rules.authorization,
362 &room_create_event,
363 fetch_state,
364 )
365 .await;
366 }
367
368 let sender_membership = sender_membership?;
370 if sender_membership != MembershipState::Join {
371 return Err!("sender's membership `{sender_membership}` is not `join`");
372 }
373
374 let current_room_power_levels_event = current_room_power_levels_event?;
375
376 let creators = room_create_event
377 .creators(&rules.authorization)
378 .map_err(auth_input_error)?;
379
380 let sender_power_level = current_room_power_levels_event
381 .user_power_level(sender, creators.clone(), &rules.authorization)
382 .map_err(auth_input_error)?;
383
384 if *incoming_event.event_type() == TimelineEventType::RoomThirdPartyInvite {
386 let invite_power_level = current_room_power_levels_event
389 .get_as_int_or_default(RoomPowerLevelsIntField::Invite, &rules.authorization)
390 .map_err(auth_input_error)?;
391
392 if sender_power_level < invite_power_level {
393 return Err!(
394 "sender does not have enough power ({sender_power_level:?}) to send invites \
395 ({invite_power_level}) in this room"
396 );
397 }
398
399 trace!("`m.room.third_party_invite` event was allowed");
400 return Ok(());
401 }
402
403 let event_type_power_level = current_room_power_levels_event
406 .event_power_level(
407 incoming_event.event_type(),
408 incoming_event.state_key(),
409 &rules.authorization,
410 )
411 .map_err(auth_input_error)?;
412
413 if sender_power_level < event_type_power_level {
414 return Err!(
415 "sender does not have enough power ({sender_power_level:?}) for `{}` event type \
416 ({event_type_power_level})",
417 incoming_event.event_type()
418 );
419 }
420
421 if incoming_event
424 .state_key()
425 .is_some_and(|k| k.starts_with('@'))
426 && incoming_event.state_key() != Some(incoming_event.sender().as_str())
427 {
428 return Err!("sender cannot send event with `state_key` matching another user's ID");
429 }
430
431 if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
433 let room_power_levels_event = RoomPowerLevelsEvent::new(incoming_event);
434
435 return check_room_power_levels(
436 &room_power_levels_event,
437 current_room_power_levels_event.as_ref(),
438 &rules.authorization,
439 sender_power_level,
440 creators,
441 );
442 }
443
444 if rules.authorization.special_case_room_redaction
446 && *incoming_event.event_type() == TimelineEventType::RoomRedaction
447 {
448 return check_room_redaction(
449 incoming_event,
450 current_room_power_levels_event.as_ref(),
451 &rules.authorization,
452 sender_power_level,
453 );
454 }
455
456 trace!("allowing event passed all checks");
458 Ok(())
459}
460
461#[tracing::instrument(level = "trace", skip_all)]
464fn check_room_create<Pdu>(
465 room_create_event: &RoomCreateEvent<Pdu>,
466 rules: &AuthorizationRules,
467) -> Result
468where
469 Pdu: Event,
470{
471 if room_create_event.prev_events().next().is_some() {
473 return Err!("`m.room.create` event cannot have previous events");
474 }
475
476 if room_create_event.auth_events().next().is_some() {
478 return Err!("`m.room.create` event cannot have `auth_events`");
479 }
480
481 if rules.room_create_event_id_as_room_id {
482 let Ok(room_create_event_id) = room_create_event.room_id().as_event_id() else {
483 return Err!(Request(InvalidParam(
484 "Failed to create `event_id` out of `m.room.create` synthetic `room_id`"
485 )));
486 };
487
488 if room_create_event_id != room_create_event.event_id() {
489 return Err!(Request(InvalidParam(
490 "`m.room.create` has mismatching synthetic `room_id` and `event_id`"
491 )));
492 }
493 } else {
494 let Some(room_id_server_name) = room_create_event.room_id().server_name() else {
497 return Err!("Invalid `ServerName` for `room_id` in `m.room.create` event");
498 };
499
500 if room_id_server_name != room_create_event.sender().server_name() {
501 return Err!(
502 "Mismatched `ServerName` for `room_id` in `m.room.create` with `sender`"
503 );
504 }
505 }
506
507 if !rules.use_room_create_sender && !room_create_event.has_creator()? {
515 return Err!("missing `creator` field in `m.room.create` event");
516 }
517
518 trace!("`m.room.create` event was allowed");
520 Ok(())
521}
522
523#[tracing::instrument(level = "trace", skip_all)]
526fn check_room_power_levels<Creators, Pdu, StatePdu>(
527 room_power_levels_event: &RoomPowerLevelsEvent<Pdu>,
528 current_room_power_levels_event: Option<&RoomPowerLevelsEvent<StatePdu>>,
529 rules: &AuthorizationRules,
530 sender_power_level: impl Into<UserPowerLevel>,
531 mut room_creators: Creators,
532) -> Result
533where
534 Creators: Iterator<Item = OwnedUserId> + Clone,
535 Pdu: Event,
536 StatePdu: Event,
537{
538 let sender_power_level = sender_power_level.into();
539
540 let new_int_fields = room_power_levels_event.int_fields_map(rules)?;
544
545 let new_events = room_power_levels_event.events(rules)?;
548 let new_notifications = room_power_levels_event.notifications(rules)?;
549
550 let new_users = room_power_levels_event.users(rules)?;
556
557 if rules.explicitly_privilege_room_creators
563 && new_users.as_ref().is_some_and(|new_users| {
564 room_creators.any(|creator| power_levels::contains_key(new_users, &creator))
565 }) {
566 return Err!(Request(InvalidParam(
567 "creator user IDs are not allowed in the `users` field"
568 )));
569 }
570
571 trace!("validation of power event finished");
572
573 let Some(current_room_power_levels_event) = current_room_power_levels_event else {
576 trace!("initial m.room.power_levels event allowed");
577 return Ok(());
578 };
579
580 for field in RoomPowerLevelsIntField::ALL {
584 let current_power_level = current_room_power_levels_event
585 .get_as_int(*field, rules)
586 .map_err(auth_input_error)?;
587
588 let new_power_level = power_levels::get_value(&new_int_fields, field).copied();
589
590 if current_power_level == new_power_level {
591 continue;
592 }
593
594 let current_power_level_too_big =
597 current_power_level.unwrap_or_else(|| field.default_value()) > sender_power_level;
598
599 let new_power_level_too_big =
602 new_power_level.unwrap_or_else(|| field.default_value()) > sender_power_level;
603
604 if current_power_level_too_big || new_power_level_too_big {
605 return Err!(
606 "sender does not have enough power to change the power level of `{field}`"
607 );
608 }
609 }
610
611 let current_events = current_room_power_levels_event
615 .events(rules)
616 .map_err(auth_input_error)?;
617
618 check_power_level_maps(
619 current_events.as_deref(),
620 new_events.as_deref(),
621 sender_power_level,
622 |_, current_power_level| {
623 current_power_level > sender_power_level
628 },
629 |ev_type| {
630 err!(
631 "sender does not have enough power to change the `{ev_type}` event type power \
632 level"
633 )
634 },
635 )?;
636
637 if rules.limit_notifications_power_levels {
642 let current_notifications = current_room_power_levels_event
643 .notifications(rules)
644 .map_err(auth_input_error)?;
645
646 check_power_level_maps(
647 current_notifications.as_deref(),
648 new_notifications.as_deref(),
649 sender_power_level,
650 |_, current_power_level| {
651 current_power_level > sender_power_level
656 },
657 |key| {
658 err!(
659 "sender does not have enough power to change the `{key}` notification power \
660 level"
661 )
662 },
663 )?;
664 }
665
666 let current_users = current_room_power_levels_event
670 .users(rules)
671 .map_err(auth_input_error)?;
672
673 check_power_level_maps(
674 current_users.as_deref(),
675 new_users.as_deref(),
676 sender_power_level,
677 |user_id, current_power_level| {
678 user_id != room_power_levels_event.sender()
683 && current_power_level >= sender_power_level
684 },
685 |user_id| err!("sender does not have enough power to change `{user_id}`'s power level"),
686 )?;
687
688 trace!("m.room.power_levels event allowed");
690 Ok(())
691}
692
693fn check_power_level_maps<'a, K>(
712 current: Option<&'a [(K, Int)]>,
713 new: Option<&'a [(K, Int)]>,
714 sender_power_level: UserPowerLevel,
715 reject_current_power_level_change_fn: impl FnOnce(&K, Int) -> bool + Copy,
716 error_fn: impl FnOnce(&K) -> Error,
717) -> Result
718where
719 K: Ord,
720{
721 let keys_to_check = current
722 .iter()
723 .flat_map(|m| m.iter().map(|(k, _)| k))
724 .chain(new.iter().flat_map(|m| m.iter().map(|(k, _)| k)));
725
726 for key in keys_to_check {
727 let current_power_level = current.and_then(|m| power_levels::get_value(m, key));
728 let new_power_level = new.and_then(|m| power_levels::get_value(m, key));
729
730 if current_power_level == new_power_level {
731 continue;
732 }
733
734 let current_power_level_change_rejected = current_power_level
736 .is_some_and(|power_level| reject_current_power_level_change_fn(key, *power_level));
737
738 let new_power_level_too_big =
741 new_power_level.is_some_and(|&new_power_level| new_power_level > sender_power_level);
742
743 if current_power_level_change_rejected || new_power_level_too_big {
744 return Err(error_fn(key));
745 }
746 }
747
748 Ok(())
749}
750
751fn check_room_redaction<Pdu, StatePdu>(
754 room_redaction_event: &Pdu,
755 current_room_power_levels_event: Option<&RoomPowerLevelsEvent<StatePdu>>,
756 rules: &AuthorizationRules,
757 sender_level: UserPowerLevel,
758) -> Result
759where
760 Pdu: Event,
761 StatePdu: Event,
762{
763 let redact_level = current_room_power_levels_event
764 .cloned()
765 .get_as_int_or_default(RoomPowerLevelsIntField::Redact, rules)
766 .map_err(auth_input_error)?;
767
768 if sender_level >= redact_level {
771 trace!("`m.room.redaction` event allowed via power levels");
772 return Ok(());
773 }
774
775 if room_redaction_event.event_id().server_name()
778 == room_redaction_event
779 .redacts()
780 .as_ref()
781 .and_then(|&id| id.server_name())
782 {
783 trace!("`m.room.redaction` event allowed via room version 1 rules");
784 return Ok(());
785 }
786
787 Err!("`m.room.redaction` event did not pass any of the allow rules")
789}
790
791pub(super) fn auth_input_error(error: Error) -> Error {
792 match error {
793 | error @ (Error::Err(..) | Error::Request(InvalidParam, ..)) =>
794 err!(Database("invalid authorization dependency: {error}")),
795 | error => error,
796 }
797}