Skip to main content

tuwunel_api/client/admin/users/
username_available.rs

1use axum::extract::State;
2use ruma::UserId;
3use synapse_admin_api::username_available::v1 as username_available;
4use tuwunel_core::{Err, Result, err};
5
6use crate::{Ruma, client::admin::require_admin};
7
8/// # `GET /_synapse/admin/v1/username_available`
9///
10/// Never returns `available: false`; an unavailable username is always an
11/// error, mirroring the client-server `/register/available` endpoint.
12pub(crate) async fn admin_username_available_route(
13	State(services): State<crate::State>,
14	body: Ruma<username_available::Request>,
15) -> Result<username_available::Response> {
16	require_admin(&services, body.sender_user()).await?;
17
18	if services
19		.config
20		.forbidden_usernames
21		.is_match(&body.username)
22	{
23		return Err!(Request(InvalidUsername("Username is forbidden")));
24	}
25
26	let user_id = UserId::parse_with_server_name(&body.username, services.globals.server_name())
27		.map_err(|_| err!(Request(InvalidUsername("Username is not a valid localpart"))))?;
28
29	if user_id.validate_strict().is_err() {
30		return Err!(Request(InvalidUsername("Username contains disallowed characters")));
31	}
32
33	if services.users.exists(&user_id).await {
34		return Err!(Request(UserInUse("Username is not available")));
35	}
36
37	if services
38		.appservice
39		.is_exclusive_user_id(&user_id)
40		.await
41	{
42		return Err!(Request(Exclusive("Username is reserved by an appservice")));
43	}
44
45	Ok(username_available::Response::new(true))
46}