1use axum::{
2 Router,
3 response::IntoResponse,
4 routing::{MethodRouter, any, get, post, put},
5};
6use const_str::{concat, join, replace};
7use http::{HeaderValue, header};
8use ruma::api::{
9 IncomingRequest,
10 client::{message::send_message_event, state::send_state_event},
11 path_builder::PathBuilder,
12};
13use tower_http::set_header::SetResponseHeaderLayer;
14use tuwunel_api::router::RouterExt;
15use tuwunel_core::{Server, err};
16
17use crate::{
18 self as client, State, put_msc4388_route, send_message_event_or_delayed_route,
19 send_state_event_or_delayed_route, sso_callback_route,
20};
21
22pub fn build(server: &Server) -> Router<State> {
26 let config = &server.config;
27 let router = Router::new();
28 let router = register_client_auth_routes(router);
29 let router = register_client_profile_and_data_routes(router);
30 let router = register_client_keys_and_backup_routes(router);
31 let router = register_client_room_routes(router);
32 let router = register_client_state_and_sync_routes(router);
33 let router = register_client_media_and_device_routes(
34 router,
35 config.media.deny_framing,
36 config.media.deny_inline_styles,
37 );
38
39 let router = register_delayed_event_routes(router);
40 let router = register_client_misc_routes(router);
41 let router = register_rendezvous_routes(router);
42 let router = if config.federation.enable {
43 router.route("/_tuwunel/local_user_count", get(client::tuwunel_local_user_count))
44 } else {
45 router
46 };
47
48 register_legacy_media_routes(
49 router,
50 config.media.allow_legacy,
51 config.media.deny_framing,
52 config.media.deny_inline_styles,
53 )
54}
55
56fn register_client_auth_routes(router: Router<State>) -> Router<State> {
57 router
58 .ruma_route(&client::get_supported_versions_route)
59 .ruma_route(&client::get_register_available_route)
60 .ruma_route(&client::register_route)
61 .ruma_route(&client::get_login_types_route)
62 .ruma_route(&client::login_route)
63 .ruma_route(&client::login_token_route)
64 .ruma_route(&client::refresh_token_route)
65 .ruma_route(&client::sso_login_route)
66 .ruma_route(&client::sso_login_with_provider_route)
67 .route("/_matrix/client/unstable/login/sso/callback/{idp_id}", get(sso_callback_route))
68 .ruma_route(&client::sso_fallback_route)
69 .ruma_route(&client::whoami_route)
70 .ruma_route(&client::logout_route)
71 .ruma_route(&client::logout_all_route)
72 .ruma_route(&client::change_password_route)
73 .ruma_route(&client::deactivate_route)
74 .ruma_route(&client::third_party_route)
75 .ruma_route(&client::add_3pid_route)
76 .ruma_route(&client::delete_3pid_route)
77 .ruma_route(&client::request_3pid_management_token_via_email_route)
78 .ruma_route(&client::request_3pid_management_token_via_msisdn_route)
79 .ruma_route(&client::request_registration_token_via_email_route)
80 .ruma_route(&client::request_password_change_token_via_email_route)
81 .ruma_route(&client::check_registration_token_validity)
82 .ruma_route(&client::create_openid_token_route)
83 .route("/_tuwunel/sso/complete.js", get(client::sso_complete_js_route))
84 .route("/_tuwunel/sso/sso.css", get(client::sso_css_route))
85}
86
87fn register_client_profile_and_data_routes(router: Router<State>) -> Router<State> {
88 router
89 .ruma_route(&client::get_profile_field_route)
90 .ruma_route(&client::set_profile_field_route)
91 .ruma_route(&client::delete_profile_field_route)
92 .ruma_route(&client::get_profile_route)
93 .ruma_route(&client::set_presence_route)
94 .ruma_route(&client::get_presence_route)
95 .ruma_route(&client::get_filter_route)
96 .ruma_route(&client::create_filter_route)
97 .ruma_route(&client::set_global_account_data_route)
98 .ruma_route(&client::set_room_account_data_route)
99 .ruma_route(&client::get_global_account_data_route)
100 .ruma_route(&client::get_room_account_data_route)
101 .ruma_route(&client::delete_global_account_data_route)
102 .ruma_route(&client::delete_room_account_data_route)
103 .ruma_route(&client::get_tags_route)
104 .ruma_route(&client::update_tag_route)
105 .ruma_route(&client::delete_tag_route)
106 .ruma_route(&client::get_pushrules_all_route)
107 .ruma_route(&client::get_pushrules_global_route)
108 .ruma_route(&client::set_pushrule_route)
109 .ruma_route(&client::get_pushrule_route)
110 .ruma_route(&client::set_pushrule_enabled_route)
111 .ruma_route(&client::get_pushrule_enabled_route)
112 .ruma_route(&client::get_pushrule_actions_route)
113 .ruma_route(&client::set_pushrule_actions_route)
114 .ruma_route(&client::delete_pushrule_route)
115 .ruma_route(&client::get_pushers_route)
116 .ruma_route(&client::set_pushers_route)
117 .ruma_route(&client::get_notifications_route)
118 .ruma_route(&client::get_capabilities_route)
119}
120
121fn register_client_keys_and_backup_routes(router: Router<State>) -> Router<State> {
122 router
123 .ruma_route(&client::upload_keys_route)
124 .ruma_route(&client::get_keys_route)
125 .ruma_route(&client::claim_keys_route)
126 .ruma_route(&client::upload_signing_keys_route)
127 .ruma_route(&client::upload_signatures_route)
128 .ruma_route(&client::get_key_changes_route)
129 .ruma_route(&client::create_backup_version_route)
130 .ruma_route(&client::update_backup_version_route)
131 .ruma_route(&client::delete_backup_version_route)
132 .ruma_route(&client::get_latest_backup_info_route)
133 .ruma_route(&client::get_backup_info_route)
134 .ruma_route(&client::add_backup_keys_route)
135 .ruma_route(&client::add_backup_keys_for_room_route)
136 .ruma_route(&client::add_backup_keys_for_session_route)
137 .ruma_route(&client::delete_backup_keys_for_room_route)
138 .ruma_route(&client::delete_backup_keys_for_session_route)
139 .ruma_route(&client::delete_backup_keys_route)
140 .ruma_route(&client::get_backup_keys_for_room_route)
141 .ruma_route(&client::get_backup_keys_for_session_route)
142 .ruma_route(&client::get_backup_keys_route)
143}
144
145fn register_client_room_routes(router: Router<State>) -> Router<State> {
146 router
147 .ruma_route(&client::appservice_ping)
148 .ruma_route(&client::set_read_marker_route)
149 .ruma_route(&client::create_receipt_route)
150 .ruma_route(&client::create_typing_event_route)
151 .ruma_route(&client::create_room_route)
152 .ruma_route(&client::redact_event_route)
153 .ruma_route(&client::report_event_route)
154 .ruma_route(&client::report_room_route)
155 .ruma_route(&client::report_user_route)
156 .ruma_route(&client::create_alias_route)
157 .ruma_route(&client::delete_alias_route)
158 .ruma_route(&client::get_alias_route)
159 .ruma_route(&client::join_room_by_id_route)
160 .ruma_route(&client::join_room_by_id_or_alias_route)
161 .ruma_route(&client::joined_members_route)
162 .ruma_route(&client::knock_room_route)
163 .ruma_route(&client::leave_room_route)
164 .ruma_route(&client::forget_room_route)
165 .ruma_route(&client::joined_rooms_route)
166 .ruma_route(&client::kick_user_route)
167 .ruma_route(&client::ban_user_route)
168 .ruma_route(&client::unban_user_route)
169 .ruma_route(&client::invite_user_route)
170 .ruma_route(&client::set_room_visibility_route)
171 .ruma_route(&client::get_room_visibility_route)
172 .ruma_route(&client::get_public_rooms_route)
173 .ruma_route(&client::get_public_rooms_filtered_route)
174 .ruma_route(&client::search_users_route)
175 .ruma_route(&client::get_member_events_route)
176 .ruma_route(&client::get_protocols_route)
177 .ruma_route(&client::get_protocol_route)
178 .ruma_route(&client::get_user_for_protocol_route)
179 .ruma_route(&client::get_location_for_protocol_route)
180 .ruma_route(&client::get_user_for_user_id_route)
181 .ruma_route(&client::get_location_for_room_alias_route)
182 .ruma_route(&client::upgrade_room_route)
183 .ruma_route(&client::get_mutual_rooms_route)
184 .ruma_route(&client::get_room_summary)
185 .route(
186 "/_matrix/client/unstable/im.nheko.summary/rooms/{room_id_or_alias}/summary",
187 get(client::get_room_summary_legacy),
188 )
189 .ruma_route(&client::room_initial_sync_route)
190 .ruma_route(&client::get_room_event_route)
191 .ruma_route(&client::get_room_aliases_route)
192}
193
194fn register_client_state_and_sync_routes(router: Router<State>) -> Router<State> {
195 let router = route_paths::<send_message_event::v3::Request>(
198 router,
199 &put(send_message_event_or_delayed_route),
200 );
201
202 let router = route_paths::<send_state_event::v3::Request>(
203 router,
204 &put(send_state_event_or_delayed_route),
205 );
206
207 router
208 .ruma_route(&client::get_state_events_route)
209 .ruma_route(&client::get_state_events_for_key_route)
210 .route(
214 "/_matrix/client/r0/rooms/{room_id}/state/{event_type}",
215 get(client::get_state_events_for_empty_key_route)
216 .put(send_state_event_or_delayed_route),
217 )
218 .route(
219 "/_matrix/client/v3/rooms/{room_id}/state/{event_type}",
220 get(client::get_state_events_for_empty_key_route)
221 .put(send_state_event_or_delayed_route),
222 )
223 .route(
225 "/_matrix/client/r0/rooms/{room_id}/state/{event_type}/",
226 get(client::get_state_events_for_empty_key_route)
227 .put(send_state_event_or_delayed_route),
228 )
229 .route(
230 "/_matrix/client/v3/rooms/{room_id}/state/{event_type}/",
231 get(client::get_state_events_for_empty_key_route)
232 .put(send_state_event_or_delayed_route),
233 )
234 .ruma_route(&client::events_route)
235 .ruma_route(&client::sync_events_route)
236 .ruma_route(&client::sync_events_v5_route)
237 .ruma_route(&client::get_context_route)
238 .ruma_route(&client::get_event_by_timestamp_route)
239 .ruma_route(&client::get_message_events_route)
240 .ruma_route(&client::search_events_route)
241 .ruma_route(&client::get_threads_route)
242 .ruma_route(&client::get_relating_events_with_rel_type_and_event_type_route)
243 .ruma_route(&client::get_relating_events_with_rel_type_route)
244 .ruma_route(&client::get_relating_events_route)
245 .ruma_route(&client::get_hierarchy_route)
246}
247
248fn route_paths<Req: IncomingRequest>(
253 router: Router<State>,
254 handler: &MethodRouter<State>,
255) -> Router<State> {
256 Req::PATH_BUILDER
257 .all_paths()
258 .fold(router, |router, path| router.route(path, handler.clone()))
259}
260
261fn register_client_media_and_device_routes(
262 router: Router<State>,
263 media_deny_framing: bool,
264 media_deny_inline_styles: bool,
265) -> Router<State> {
266 let media_content_router = Router::new()
267 .ruma_route(&client::get_content_thumbnail_route)
268 .ruma_route(&client::get_content_route)
269 .ruma_route(&client::get_content_as_filename_route);
270
271 let media_content_router =
272 media_content_headers(media_content_router, media_deny_framing, media_deny_inline_styles);
273
274 router
275 .ruma_route(&client::create_content_route)
276 .ruma_route(&client::create_mxc_uri_route)
277 .ruma_route(&client::create_content_async_route)
278 .ruma_route(&client::get_media_preview_route)
279 .ruma_route(&client::get_media_config_route)
280 .ruma_route(&client::get_devices_route)
281 .ruma_route(&client::get_device_route)
282 .ruma_route(&client::update_device_route)
283 .ruma_route(&client::delete_device_route)
284 .ruma_route(&client::delete_devices_route)
285 .ruma_route(&client::put_dehydrated_device_route)
286 .ruma_route(&client::delete_dehydrated_device_route)
287 .ruma_route(&client::get_dehydrated_device_route)
288 .ruma_route(&client::get_dehydrated_events_route)
289 .ruma_route(&client::get_dehydrated_events_by_body_route)
290 .ruma_route(&client::send_event_to_device_route)
291 .merge(media_content_router)
292}
293
294fn register_delayed_event_routes(router: Router<State>) -> Router<State> {
295 router
296 .ruma_route(&client::send_delayed_event_route)
297 .ruma_route(&client::update_delayed_event_route)
298 .ruma_route(&client::update_delayed_event_by_body_route)
299 .ruma_route(&client::get_delayed_event_route)
300 .ruma_route(&client::get_delayed_events_route)
301}
302
303fn register_client_misc_routes(router: Router<State>) -> Router<State> {
304 router
305 .ruma_route(&client::turn_server_route)
306 .ruma_route(&client::get_transports_route)
307 .ruma_route(&client::well_known_support)
308 .ruma_route(&client::well_known_client)
309 .ruma_route(&client::tuwunel_remote_version)
310 .route("/_tuwunel/server_version", get(client::tuwunel_server_version))
311 .route(
312 "/_tuwunel/3pid/email/validate",
313 get(client::get_email_validate_route).post(client::post_email_validate_route),
314 )
315}
316
317fn register_rendezvous_routes(router: Router<State>) -> Router<State> {
318 let router = router
319 .ruma_route(&client::discover_msc4388_route)
320 .ruma_route(&client::create_msc4388_route)
321 .ruma_route(&client::get_msc4388_route)
322 .ruma_route(&client::delete_msc4388_route)
323 .route(
324 "/_matrix/client/unstable/io.element.msc4388/rendezvous/{id}/{txn_id}",
325 put(put_msc4388_route),
326 );
327
328 let session_routes = get(client::get_rendezvous_route)
329 .put(client::put_rendezvous_route)
330 .delete(client::delete_rendezvous_route);
331
332 router
333 .route(
334 "/_matrix/client/unstable/org.matrix.msc4108/rendezvous",
335 post(client::create_rendezvous_route),
336 )
337 .route("/_matrix/client/unstable/org.matrix.msc4108/rendezvous/{id}", session_routes)
338}
339
340fn register_legacy_media_routes(
341 router: Router<State>,
342 allow_legacy_media: bool,
343 media_deny_framing: bool,
344 media_deny_inline_styles: bool,
345) -> Router<State> {
346 if allow_legacy_media {
347 let media_content_router = Router::new()
348 .route(
349 "/_matrix/media/r0/download/{server_name}/{media_id}",
350 get(client::get_content_legacy_route),
351 )
352 .route(
353 "/_matrix/media/v3/download/{server_name}/{media_id}",
354 get(client::get_content_legacy_route),
355 )
356 .route(
357 "/_matrix/media/r0/download/{server_name}/{media_id}/{filename}",
358 get(client::get_content_as_filename_legacy_route),
359 )
360 .route(
361 "/_matrix/media/v3/download/{server_name}/{media_id}/{filename}",
362 get(client::get_content_as_filename_legacy_route),
363 )
364 .route(
365 "/_matrix/media/r0/thumbnail/{server_name}/{media_id}",
366 get(client::get_content_thumbnail_legacy_route),
367 )
368 .route(
369 "/_matrix/media/v3/thumbnail/{server_name}/{media_id}",
370 get(client::get_content_thumbnail_legacy_route),
371 );
372
373 let media_content_router = media_content_headers(
374 media_content_router,
375 media_deny_framing,
376 media_deny_inline_styles,
377 );
378
379 router
380 .ruma_route(&client::get_media_config_legacy_route)
381 .ruma_route(&client::get_media_preview_legacy_route)
382 .merge(media_content_router)
383 } else {
384 router
385 .route("/_matrix/media/v3/config", any(legacy_media_disabled))
386 .route("/_matrix/media/v3/download/{*path}", any(legacy_media_disabled))
387 .route("/_matrix/media/v3/thumbnail/{*path}", any(legacy_media_disabled))
388 .route("/_matrix/media/v3/preview_url", any(legacy_media_disabled))
389 }
390}
391
392fn media_content_headers(
393 router: Router<State>,
394 deny_framing: bool,
395 deny_inline_styles: bool,
396) -> Router<State> {
397 const MEDIA_CSP: &[&str] = &[
399 "sandbox",
400 "default-src 'none'",
401 "script-src 'none'",
402 "font-src 'none'",
403 "frame-ancestors 'none'",
404 "form-action 'none'",
405 "base-uri 'none'",
406 ];
407
408 const POLICY: &str = join!(MEDIA_CSP, ";");
409 const FRAMING_POLICY: &str = replace!(POLICY, "frame-ancestors 'none';", "");
410 const STYLED_POLICY: &str = concat!(POLICY, ";style-src 'unsafe-inline'");
411 const FRAMING_STYLED_POLICY: &str = concat!(FRAMING_POLICY, ";style-src 'unsafe-inline'");
412
413 let policy = match (deny_framing, deny_inline_styles) {
414 | (false, false) => FRAMING_STYLED_POLICY,
415 | (false, true) => FRAMING_POLICY,
416 | (true, false) => STYLED_POLICY,
417 | (true, true) => POLICY,
418 };
419
420 router.route_layer(SetResponseHeaderLayer::overriding(
421 header::CONTENT_SECURITY_POLICY,
422 HeaderValue::from_static(policy),
423 ))
424}
425
426async fn legacy_media_disabled() -> impl IntoResponse {
427 err!(Request(Forbidden("Unauthenticated media is disabled.")))
428}