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