Skip to main content

tuwunel_api/client/register/
available.rs

1use axum::extract::State;
2use ruma::{UserId, api::client::account::get_username_availability};
3use tuwunel_core::{Err, Result};
4
5use super::is_matrix_appservice_irc;
6use crate::{ClientIp, Ruma};
7
8/// # `GET /_matrix/client/v3/register/available`
9///
10/// Checks if a username is valid and available on this server.
11///
12/// Conditions for returning true:
13/// - The user id is not historical
14/// - The server name of the user id matches this server
15/// - No user or appservice on this server already claimed this username
16///
17/// Note: This will not reserve the username, so the username might become
18/// invalid when trying to register
19#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
20pub(crate) async fn get_register_available_route(
21	State(services): State<crate::State>,
22	ClientIp(client): ClientIp,
23	body: Ruma<get_username_availability::v3::Request>,
24) -> Result<get_username_availability::v3::Response> {
25	let is_irc = is_matrix_appservice_irc(body.appservice_info.as_ref());
26
27	if services
28		.config
29		.forbidden_usernames
30		.is_match(&body.username)
31	{
32		return Err!(Request(Forbidden("Username is forbidden")));
33	}
34
35	// don't force the username lowercase if it's from matrix-appservice-irc
36	let body_username = if is_irc {
37		body.username.clone()
38	} else {
39		body.username.to_lowercase()
40	};
41
42	// Validate user id
43	let user_id =
44		match UserId::parse_with_server_name(&body_username, services.globals.server_name()) {
45			| Ok(user_id) => {
46				if let Err(e) = user_id.validate_strict() {
47					// unless the username is from the broken matrix appservice IRC bridge, we
48					// should follow synapse's behaviour on not allowing things like spaces
49					// and UTF-8 characters in usernames
50					if !is_irc {
51						return Err!(Request(InvalidUsername(debug_warn!(
52							"Username {body_username} contains disallowed characters or spaces: \
53							 {e}"
54						))));
55					}
56				}
57
58				user_id
59			},
60			| Err(e) => {
61				return Err!(Request(InvalidUsername(debug_warn!(
62					"Username {body_username} is not valid: {e}"
63				))));
64			},
65		};
66
67	// Check if username is creative enough
68	if services.users.exists(&user_id).await {
69		return Err!(Request(UserInUse("User ID is not available.")));
70	}
71
72	if let Some(ref info) = body.appservice_info
73		&& !info.is_user_match(&user_id)
74	{
75		return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
76	}
77
78	if services
79		.appservice
80		.is_exclusive_user_id(&user_id)
81		.await
82	{
83		return Err!(Request(Exclusive("Username is reserved by an appservice.")));
84	}
85
86	Ok(get_username_availability::v3::Response { available: true })
87}