1mod allow_cross_signing_reset;
2mod delete_device;
3mod delete_user;
4mod is_localpart_available;
5mod provision_user;
6mod query_user;
7mod reactivate_user;
8pub mod routes;
12mod set_displayname;
13mod sync_devices;
14mod unset_displayname;
15mod update_device_display_name;
16mod upsert_device;
17
18use axum::{RequestPartsExt, extract::FromRequestParts};
19use axum_extra::{
20 TypedHeader,
21 headers::{Authorization, authorization::Bearer},
22};
23use http::request::Parts;
24use log as _;
25use ruma::{OwnedUserId, UserId};
26use tuwunel_api::router::{Ruma, State};
27use tuwunel_core::{Err, Error, Result, err, mod_ctor, mod_dtor};
28
29pub(crate) use self::{
30 allow_cross_signing_reset::allow_cross_signing_reset_route,
31 delete_device::delete_device_route, delete_user::delete_user_route,
32 is_localpart_available::is_localpart_available_route, provision_user::provision_user_route,
33 query_user::query_user_route, reactivate_user::reactivate_user_route,
34 set_displayname::set_displayname_route, sync_devices::sync_devices_route,
35 unset_displayname::unset_displayname_route,
36 update_device_display_name::update_device_display_name_route,
37 upsert_device::upsert_device_route,
38};
39
40pub(crate) struct Mas;
44
45impl FromRequestParts<State> for Mas {
46 type Rejection = Error;
47
48 async fn from_request_parts(
49 parts: &mut Parts,
50 services: &State,
51 ) -> Result<Self, Self::Rejection> {
52 let secret = services
53 .config
54 .authentication
55 .mas_secret
56 .as_deref()
57 .filter(|secret| !secret.is_empty());
58
59 let bearer = parts
60 .extract::<TypedHeader<Authorization<Bearer>>>()
61 .await
62 .ok();
63
64 let token = bearer
65 .as_ref()
66 .map(|TypedHeader(Authorization(bearer))| bearer.token());
67
68 match (secret, token) {
69 | (Some(secret), Some(token)) if secret == token => Ok(Self),
70 | _ => Err!(Request(Forbidden("This endpoint may only be called by MAS"))),
71 }
72 }
73}
74
75pub(crate) fn local_user(services: State, localpart: &str) -> Result<OwnedUserId> {
78 UserId::parse_with_server_name(localpart, services.globals.server_name())
79 .map_err(|_| err!(Request(InvalidParam("Invalid localpart"))))
80}
81
82pub(crate) async fn existing_user(services: State, localpart: &str) -> Result<OwnedUserId> {
85 let user_id = local_user(services, localpart)?;
86
87 services
88 .users
89 .exists(&user_id)
90 .await
91 .then_some(user_id)
92 .ok_or_else(|| err!(Request(NotFound("User does not exist"))))
93}
94
95mod_ctor! {}
96mod_dtor! {}