1mod auth_types;
2mod room_member;
3#[cfg(test)]
4mod tests;
5
6use futures::{
7 FutureExt, TryStreamExt,
8 future::{join3, try_join},
9};
10use ruma::{
11 EventId, Int, OwnedEventId, 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, StateKey},
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 FetchStateExt, TypeStateKey, events,
32 events::{
33 RoomCreateEvent, RoomMemberEvent, RoomPowerLevelsEvent,
34 power_levels::{self, RoomPowerLevelsEventOptionExt, RoomPowerLevelsIntField},
35 },
36};
37
38#[tracing::instrument(
39 level = "debug",
40 skip_all,
41 fields(
42 event_id = ?incoming_event.event_id(),
43 )
44)]
45pub async fn auth_check<FetchEvent, EventFut, FetchState, StateFut, Pdu>(
46 rules: &RoomVersionRules,
47 incoming_event: &Pdu,
48 fetch_event: &FetchEvent,
49 fetch_state: &FetchState,
50) -> Result
51where
52 FetchEvent: Fn(OwnedEventId) -> EventFut + Sync,
53 EventFut: Future<Output = Result<Pdu>> + Send,
54 FetchState: Fn(StateEventType, StateKey) -> StateFut + Sync,
55 StateFut: Future<Output = Result<Pdu>> + Send,
56 Pdu: Event,
57{
58 let dependent = check_state_dependent_auth_rules(rules, incoming_event, fetch_state);
59
60 let independent = check_state_independent_auth_rules(rules, incoming_event, fetch_event);
61
62 match try_join(independent, dependent).await {
63 | Err(e) if matches!(e, Error::Request(InvalidParam, ..)) => Err(e),
64 | Err(e) => Err!(Request(Forbidden("Auth check failed: {e}"))),
65 | Ok(_) => Ok(()),
66 }
67}
68
69#[tracing::instrument(
85 name = "independent",
86 level = "debug",
87 skip_all,
88 fields(
89 sender = ?incoming_event.sender(),
90 )
91)]
92pub(super) async fn check_state_independent_auth_rules<Fetch, Fut, Pdu>(
93 rules: &RoomVersionRules,
94 incoming_event: &Pdu,
95 fetch_event: &Fetch,
96) -> Result
97where
98 Fetch: Fn(OwnedEventId) -> Fut + Sync,
99 Fut: Future<Output = Result<Pdu>> + Send,
100 Pdu: Event,
101{
102 if *incoming_event.event_type() == TimelineEventType::RoomCreate {
104 let room_create_event = RoomCreateEvent::new(incoming_event.clone());
105 return check_room_create(&room_create_event, &rules.authorization);
106 }
107
108 let expected_auth_types = auth_types_for_event(
109 incoming_event.event_type(),
110 incoming_event.sender(),
111 incoming_event.state_key(),
112 incoming_event.content(),
113 &rules.authorization,
114 false,
115 )?;
116
117 let seen_auth_types = Vec::with_capacity(expected_auth_types.len());
119 let seen_auth_types = incoming_event
120 .auth_events()
121 .try_stream()
122 .and_then(async |event_id: &EventId| match fetch_event(event_id.to_owned()).await {
123 | Ok(auth_event) => Ok(auth_event),
124 | Err(e) if e.is_not_found() => Err!(Request(NotFound("auth event {event_id}: {e}"))),
125 | Err(e) => Err(e),
126 })
127 .ready_try_fold(seen_auth_types, |mut seen_auth_types, auth_event| {
128 let event_id = auth_event.event_id();
129
130 if auth_event.room_id() != incoming_event.room_id() {
132 return Err!("auth event {event_id} not in the same room");
133 }
134
135 let state_key = auth_event
136 .state_key()
137 .ok_or_else(|| err!("auth event {event_id} has no `state_key`"))?;
138
139 let event_type = auth_event.event_type();
140 let key: TypeStateKey = (event_type.to_cow_str().into(), state_key.into());
141
142 if seen_auth_types.contains(&key) {
145 return Err!(
146 "duplicate auth event {event_id} for ({event_type}, {state_key}) pair"
147 );
148 }
149
150 if !expected_auth_types.contains(&key) {
154 return Err!(
155 "unexpected auth event {event_id} with ({event_type}, {state_key}) pair"
156 );
157 }
158
159 if auth_event.rejected() {
162 return Err!("rejected auth event {event_id}");
163 }
164
165 seen_auth_types.push(key);
166 Ok(seen_auth_types)
167 })
168 .await?;
169
170 if !rules
172 .authorization
173 .room_create_event_id_as_room_id
174 && !seen_auth_types
175 .iter()
176 .any(|(event_type, _)| *event_type == StateEventType::RoomCreate)
177 {
178 return Err!("no `m.room.create` event in auth events");
179 }
180
181 if rules
184 .authorization
185 .room_create_event_id_as_room_id
186 {
187 let room_create_event_id = incoming_event
188 .room_id()
189 .as_event_id()
190 .map_err(|e| {
191 err!(Request(InvalidParam(
192 "could not construct `m.room.create` event ID from room ID: {e}"
193 )))
194 })?;
195
196 let Ok(room_create_event) = fetch_event(room_create_event_id.clone()).await else {
197 return Err!(Request(NotFound(
198 "failed to find `m.room.create` event {room_create_event_id}"
199 )));
200 };
201
202 if room_create_event.rejected() {
203 return Err!("rejected `m.room.create` event {room_create_event_id}");
204 }
205 }
206
207 Ok(())
208}
209
210#[tracing::instrument(
235 name = "dependent",
236 level = "debug",
237 skip_all,
238 fields(
239 sender = ?incoming_event.sender(),
240 )
241)]
242pub(super) async fn check_state_dependent_auth_rules<Fetch, Fut, Pdu>(
243 rules: &RoomVersionRules,
244 incoming_event: &Pdu,
245 fetch_state: &Fetch,
246) -> Result
247where
248 Fetch: Fn(StateEventType, StateKey) -> Fut + Sync,
249 Fut: Future<Output = Result<Pdu>> + Send,
250 Pdu: Event,
251{
252 if *incoming_event.event_type() == TimelineEventType::RoomCreate {
254 trace!("allowing `m.room.create` event");
255 return Ok(());
256 }
257
258 let sender = incoming_event.sender();
259 let (room_create_event, sender_membership, current_room_power_levels_event) = join3(
260 fetch_state.room_create_event(),
261 fetch_state.user_membership(sender),
262 fetch_state.room_power_levels_event(),
263 )
264 .await;
265
266 let room_create_event = room_create_event?;
270 let federate = room_create_event.federate()?;
271 if !federate
272 && room_create_event.sender().server_name() != incoming_event.sender().server_name()
273 {
274 return Err!(
275 "room is not federated and event's sender domain does not match `m.room.create` \
276 event's sender domain"
277 );
278 }
279
280 if rules.authorization.special_case_room_aliases
282 && incoming_event.event_type().to_cow_str() == "m.room.aliases"
283 {
284 trace!("starting m.room.aliases check");
285 if incoming_event.state_key() != Some(sender.server_name().as_str()) {
289 return Err!(
290 "server name of the `state_key` of `m.room.aliases` event does not match the \
291 server name of the sender"
292 );
293 }
294
295 trace!("`m.room.aliases` event was allowed");
297 return Ok(());
298 }
299
300 if *incoming_event.event_type() == TimelineEventType::RoomMember {
302 let room_member_event = RoomMemberEvent::new(incoming_event.clone());
303 return check_room_member(
304 &room_member_event,
305 &rules.authorization,
306 &room_create_event,
307 fetch_state,
308 )
309 .boxed()
310 .await;
311 }
312
313 let sender_membership = sender_membership?;
315 if sender_membership != MembershipState::Join {
316 return Err!("sender's membership `{sender_membership}` is not `join`");
317 }
318
319 let creators = room_create_event.creators(&rules.authorization)?;
320 let sender_power_level = current_room_power_levels_event.user_power_level(
321 sender,
322 creators.clone(),
323 &rules.authorization,
324 )?;
325
326 if *incoming_event.event_type() == TimelineEventType::RoomThirdPartyInvite {
328 let invite_power_level = current_room_power_levels_event
331 .get_as_int_or_default(RoomPowerLevelsIntField::Invite, &rules.authorization)?;
332
333 if sender_power_level < invite_power_level {
334 return Err!(
335 "sender does not have enough power ({sender_power_level:?}) to send invites \
336 ({invite_power_level}) in this room"
337 );
338 }
339
340 trace!("`m.room.third_party_invite` event was allowed");
341 return Ok(());
342 }
343
344 let event_type_power_level = current_room_power_levels_event.event_power_level(
347 incoming_event.event_type(),
348 incoming_event.state_key(),
349 &rules.authorization,
350 )?;
351
352 if sender_power_level < event_type_power_level {
353 return Err!(
354 "sender does not have enough power ({sender_power_level:?}) for `{}` event type \
355 ({event_type_power_level})",
356 incoming_event.event_type()
357 );
358 }
359
360 if incoming_event
363 .state_key()
364 .is_some_and(|k| k.starts_with('@'))
365 && incoming_event.state_key() != Some(incoming_event.sender().as_str())
366 {
367 return Err!("sender cannot send event with `state_key` matching another user's ID");
368 }
369
370 if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
372 let room_power_levels_event = RoomPowerLevelsEvent::new(incoming_event.clone());
373 return check_room_power_levels(
374 &room_power_levels_event,
375 current_room_power_levels_event.as_ref(),
376 &rules.authorization,
377 sender_power_level,
378 creators,
379 );
380 }
381
382 if rules.authorization.special_case_room_redaction
384 && *incoming_event.event_type() == TimelineEventType::RoomRedaction
385 {
386 return check_room_redaction(
387 incoming_event,
388 current_room_power_levels_event.as_ref(),
389 &rules.authorization,
390 sender_power_level,
391 );
392 }
393
394 trace!("allowing event passed all checks");
396 Ok(())
397}
398
399#[tracing::instrument(level = "trace", skip_all)]
402fn check_room_create<Pdu>(
403 room_create_event: &RoomCreateEvent<Pdu>,
404 rules: &AuthorizationRules,
405) -> Result
406where
407 Pdu: Event,
408{
409 if room_create_event.prev_events().next().is_some() {
411 return Err!("`m.room.create` event cannot have previous events");
412 }
413
414 if rules.room_create_event_id_as_room_id {
415 let Ok(room_create_event_id) = room_create_event.room_id().as_event_id() else {
416 return Err!(Request(InvalidParam(
417 "Failed to create `event_id` out of `m.room.create` synthetic `room_id`"
418 )));
419 };
420
421 if room_create_event_id != room_create_event.event_id() {
422 return Err!(Request(InvalidParam(
423 "`m.room.create` has mismatching synthetic `room_id` and `event_id`"
424 )));
425 }
426 } else {
427 let Some(room_id_server_name) = room_create_event.room_id().server_name() else {
430 return Err!("Invalid `ServerName` for `room_id` in `m.room.create` event");
431 };
432
433 if room_id_server_name != room_create_event.sender().server_name() {
434 return Err!(
435 "Mismatched `ServerName` for `room_id` in `m.room.create` with `sender`"
436 );
437 }
438 }
439
440 if !rules.use_room_create_sender && !room_create_event.has_creator()? {
448 return Err!("missing `creator` field in `m.room.create` event");
449 }
450
451 trace!("`m.room.create` event was allowed");
453 Ok(())
454}
455
456#[tracing::instrument(level = "trace", skip_all)]
459fn check_room_power_levels<Creators, Pdu>(
460 room_power_levels_event: &RoomPowerLevelsEvent<Pdu>,
461 current_room_power_levels_event: Option<&RoomPowerLevelsEvent<Pdu>>,
462 rules: &AuthorizationRules,
463 sender_power_level: impl Into<UserPowerLevel>,
464 mut room_creators: Creators,
465) -> Result
466where
467 Creators: Iterator<Item = OwnedUserId> + Clone,
468 Pdu: Event,
469{
470 let sender_power_level = sender_power_level.into();
471
472 let new_int_fields = room_power_levels_event.int_fields_map(rules)?;
476
477 let new_events = room_power_levels_event.events(rules)?;
480 let new_notifications = room_power_levels_event.notifications(rules)?;
481
482 let new_users = room_power_levels_event.users(rules)?;
488
489 if rules.explicitly_privilege_room_creators
495 && new_users.as_ref().is_some_and(|new_users| {
496 room_creators.any(|creator| power_levels::contains_key(new_users, &creator))
497 }) {
498 return Err!(Request(InvalidParam(
499 "creator user IDs are not allowed in the `users` field"
500 )));
501 }
502
503 trace!("validation of power event finished");
504
505 let Some(current_room_power_levels_event) = current_room_power_levels_event else {
508 trace!("initial m.room.power_levels event allowed");
509 return Ok(());
510 };
511
512 for field in RoomPowerLevelsIntField::ALL {
516 let current_power_level = current_room_power_levels_event.get_as_int(*field, rules)?;
517 let new_power_level = power_levels::get_value(&new_int_fields, field).copied();
518
519 if current_power_level == new_power_level {
520 continue;
521 }
522
523 let current_power_level_too_big =
526 current_power_level.unwrap_or_else(|| field.default_value()) > sender_power_level;
527
528 let new_power_level_too_big =
531 new_power_level.unwrap_or_else(|| field.default_value()) > sender_power_level;
532
533 if current_power_level_too_big || new_power_level_too_big {
534 return Err!(
535 "sender does not have enough power to change the power level of `{field}`"
536 );
537 }
538 }
539
540 let current_events = current_room_power_levels_event.events(rules)?;
544 check_power_level_maps(
545 current_events.as_deref(),
546 new_events.as_deref(),
547 sender_power_level,
548 |_, current_power_level| {
549 current_power_level > sender_power_level
554 },
555 |ev_type| {
556 err!(
557 "sender does not have enough power to change the `{ev_type}` event type power \
558 level"
559 )
560 },
561 )?;
562
563 if rules.limit_notifications_power_levels {
568 let current_notifications = current_room_power_levels_event.notifications(rules)?;
569 check_power_level_maps(
570 current_notifications.as_deref(),
571 new_notifications.as_deref(),
572 sender_power_level,
573 |_, current_power_level| {
574 current_power_level > sender_power_level
579 },
580 |key| {
581 err!(
582 "sender does not have enough power to change the `{key}` notification power \
583 level"
584 )
585 },
586 )?;
587 }
588
589 let current_users = current_room_power_levels_event.users(rules)?;
593 check_power_level_maps(
594 current_users.as_deref(),
595 new_users.as_deref(),
596 sender_power_level,
597 |user_id, current_power_level| {
598 user_id != room_power_levels_event.sender()
603 && current_power_level >= sender_power_level
604 },
605 |user_id| err!("sender does not have enough power to change `{user_id}`'s power level"),
606 )?;
607
608 trace!("m.room.power_levels event allowed");
610 Ok(())
611}
612
613fn check_power_level_maps<'a, K>(
632 current: Option<&'a [(K, Int)]>,
633 new: Option<&'a [(K, Int)]>,
634 sender_power_level: UserPowerLevel,
635 reject_current_power_level_change_fn: impl FnOnce(&K, Int) -> bool + Copy,
636 error_fn: impl FnOnce(&K) -> Error,
637) -> Result
638where
639 K: Ord,
640{
641 let keys_to_check = current
642 .iter()
643 .flat_map(|m| m.iter().map(|(k, _)| k))
644 .chain(new.iter().flat_map(|m| m.iter().map(|(k, _)| k)));
645
646 for key in keys_to_check {
647 let current_power_level = current.and_then(|m| power_levels::get_value(m, key));
648 let new_power_level = new.and_then(|m| power_levels::get_value(m, key));
649
650 if current_power_level == new_power_level {
651 continue;
652 }
653
654 let current_power_level_change_rejected = current_power_level
656 .is_some_and(|power_level| reject_current_power_level_change_fn(key, *power_level));
657
658 let new_power_level_too_big =
661 new_power_level.is_some_and(|&new_power_level| new_power_level > sender_power_level);
662
663 if current_power_level_change_rejected || new_power_level_too_big {
664 return Err(error_fn(key));
665 }
666 }
667
668 Ok(())
669}
670
671fn check_room_redaction<Pdu>(
674 room_redaction_event: &Pdu,
675 current_room_power_levels_event: Option<&RoomPowerLevelsEvent<Pdu>>,
676 rules: &AuthorizationRules,
677 sender_level: UserPowerLevel,
678) -> Result
679where
680 Pdu: Event,
681{
682 let redact_level = current_room_power_levels_event
683 .cloned()
684 .get_as_int_or_default(RoomPowerLevelsIntField::Redact, rules)?;
685
686 if sender_level >= redact_level {
689 trace!("`m.room.redaction` event allowed via power levels");
690 return Ok(());
691 }
692
693 if room_redaction_event.event_id().server_name()
696 == room_redaction_event
697 .redacts()
698 .as_ref()
699 .and_then(|&id| id.server_name())
700 {
701 trace!("`m.room.redaction` event allowed via room version 1 rules");
702 return Ok(());
703 }
704
705 Err!("`m.room.redaction` event did not pass any of the allow rules")
707}