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