Skip to main content

tuwunel_service/rooms/state_res/
fetch_state.rs

1use std::{collections::BTreeMap, future::ready};
2
3use futures::{FutureExt, TryFutureExt};
4use ruma::{
5	UserId,
6	events::{StateEventType, room::member::MembershipState},
7};
8use tuwunel_core::{
9	Error, Result, err,
10	matrix::{Event, PduEvent, StateKey, TypeStateKey},
11	result::NotFound,
12};
13
14use super::{
15	StateMap,
16	event_auth::auth_input_error,
17	events::{
18		JoinRule, RoomCreateEvent, RoomJoinRulesEvent, RoomMemberEvent, RoomPowerLevelsEvent,
19		RoomThirdPartyInviteEvent, member::RoomMemberEventResultExt,
20	},
21};
22
23/// Reads authorization state through a copyable handle.
24///
25/// Implementations may return owned events or borrow events held by the state snapshot.
26pub trait FetchState: Copy + Send + Sync {
27	/// Event representation returned by this state snapshot.
28	///
29	/// Borrowed representations avoid cloning events during authorization.
30	type Pdu: Event;
31
32	/// Reads the event for an exact state tuple.
33	///
34	/// Missing tuples return a not-found error under the snapshot's completeness policy.
35	fn get(
36		self,
37		ty: StateEventType,
38		key: StateKey,
39	) -> impl Future<Output = Result<Self::Pdu>> + Send;
40
41	/// Reads the room creation event.
42	///
43	/// Missing creation state is an authorization input error.
44	fn room_create_event(
45		self,
46	) -> impl Future<Output = Result<RoomCreateEvent<Self::Pdu>>> + Send {
47		self.get(StateEventType::RoomCreate, "".into())
48			.map_err(auth_input_error)
49			.map_ok(RoomCreateEvent::new)
50			.map_err(required("m.room.create"))
51	}
52
53	/// Reads a user membership from the state snapshot.
54	///
55	/// Missing membership follows the member-event defaulting rules.
56	fn user_membership(
57		self,
58		user_id: &UserId,
59	) -> impl Future<Output = Result<MembershipState>> + Send {
60		self.get(StateEventType::RoomMember, user_id.as_str().into())
61			.map_err(auth_input_error)
62			.map_ok(RoomMemberEvent::new)
63			.map(RoomMemberEventResultExt::membership)
64			.map_err(auth_input_error)
65	}
66
67	/// Reads the optional room power levels event.
68	///
69	/// Missing power levels return no event.
70	fn room_power_levels_event(
71		self,
72	) -> impl Future<Output = Result<Option<RoomPowerLevelsEvent<Self::Pdu>>>> + Send {
73		self.get(StateEventType::RoomPowerLevels, "".into())
74			.map_err(auth_input_error)
75			.map_ok(RoomPowerLevelsEvent::new)
76			.map(NotFound::optional)
77	}
78
79	/// Reads the current room join rule.
80	///
81	/// Missing join-rule state is an authorization input error.
82	fn join_rule(self) -> impl Future<Output = Result<JoinRule>> + Send {
83		self.get(StateEventType::RoomJoinRules, "".into())
84			.map_err(auth_input_error)
85			.map_ok(RoomJoinRulesEvent::new)
86			.map_err(required("m.room.join_rules"))
87			.and_then(|event| ready(event.join_rule().map_err(auth_input_error)))
88	}
89
90	/// Reads the invitation matching a third-party token.
91	///
92	/// Missing invitation state returns no event.
93	fn room_third_party_invite_event(
94		self,
95		token: &str,
96	) -> impl Future<Output = Result<Option<RoomThirdPartyInviteEvent<Self::Pdu>>>> + Send {
97		self.get(StateEventType::RoomThirdPartyInvite, token.into())
98			.map_err(auth_input_error)
99			.map_ok(RoomThirdPartyInviteEvent::new)
100			.map(NotFound::optional)
101	}
102}
103
104/// Promotes a missing required state event to an authorization input error.
105fn required(name: &'static str) -> impl Fn(Error) -> Error {
106	move |error| {
107		if error.is_not_found() {
108			err!("no `{name}` event in current state: {error}")
109		} else {
110			error
111		}
112	}
113}
114
115impl<'a> FetchState for &'a StateMap<PduEvent> {
116	type Pdu = &'a PduEvent;
117
118	fn get(
119		self,
120		ty: StateEventType,
121		key: StateKey,
122	) -> impl Future<Output = Result<Self::Pdu>> + Send {
123		ready(
124			BTreeMap::get(self, &(ty, key))
125				.ok_or_else(|| err!(Request(NotFound("Missing state event")))),
126		)
127	}
128}
129
130impl<'a> FetchState for &'a [(TypeStateKey, PduEvent)] {
131	type Pdu = &'a PduEvent;
132
133	fn get(
134		self,
135		ty: StateEventType,
136		key: StateKey,
137	) -> impl Future<Output = Result<Self::Pdu>> + Send {
138		ready(
139			self.iter()
140				.find(|((event_type, state_key), _)| *event_type == ty && *state_key == key)
141				.map(|(_, event)| event)
142				.ok_or_else(|| err!(Request(NotFound("Missing auth_event {ty:?},{key:?}")))),
143		)
144	}
145}