1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-07 17:03:01 +03:00

Check for username availability upon registration

This commit is contained in:
Quentin Gliech
2024-02-28 16:19:46 +01:00
parent 452f4c17f5
commit 896ed3f024
3 changed files with 38 additions and 5 deletions

View File

@@ -320,6 +320,7 @@ where
PasswordManager: FromRef<S>, PasswordManager: FromRef<S>,
MetadataCache: FromRef<S>, MetadataCache: FromRef<S>,
SiteConfig: FromRef<S>, SiteConfig: FromRef<S>,
BoxHomeserverConnection: FromRef<S>,
BoxClock: FromRequestParts<S>, BoxClock: FromRequestParts<S>,
BoxRng: FromRequestParts<S>, BoxRng: FromRequestParts<S>,
Policy: FromRequestParts<S>, Policy: FromRequestParts<S>,

View File

@@ -26,6 +26,7 @@ use mas_axum_utils::{
}; };
use mas_data_model::{User, UserAgent}; use mas_data_model::{User, UserAgent};
use mas_jose::jwt::Jwt; use mas_jose::jwt::Jwt;
use mas_matrix::BoxHomeserverConnection;
use mas_policy::Policy; use mas_policy::Policy;
use mas_router::UrlBuilder; use mas_router::UrlBuilder;
use mas_storage::{ use mas_storage::{
@@ -94,6 +95,9 @@ pub(crate) enum RouteError {
#[error("Invalid form action")] #[error("Invalid form action")]
InvalidFormAction, InvalidFormAction,
#[error("Homeserver connection error")]
HomeserverConnection(#[source] anyhow::Error),
#[error(transparent)] #[error(transparent)]
Internal(Box<dyn std::error::Error + Send + Sync + 'static>), Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
} }
@@ -196,6 +200,7 @@ pub(crate) async fn get(
PreferredLanguage(locale): PreferredLanguage, PreferredLanguage(locale): PreferredLanguage,
State(templates): State<Templates>, State(templates): State<Templates>,
State(url_builder): State<UrlBuilder>, State(url_builder): State<UrlBuilder>,
State(homeserver): State<BoxHomeserverConnection>,
cookie_jar: CookieJar, cookie_jar: CookieJar,
user_agent: Option<TypedHeader<headers::UserAgent>>, user_agent: Option<TypedHeader<headers::UserAgent>>,
Path(link_id): Path<Ulid>, Path(link_id): Path<Ulid>,
@@ -406,10 +411,17 @@ pub(crate) async fn get(
// form, but this lead to poor UX. This is why we do // form, but this lead to poor UX. This is why we do
// it ahead of time here. // it ahead of time here.
let maybe_existing_user = repo.user().find_by_username(&localpart).await?; let maybe_existing_user = repo.user().find_by_username(&localpart).await?;
if let Some(existing_user) = maybe_existing_user { let is_available = homeserver
// The mapper returned a username which already exists, but isn't linked .is_localpart_available(&localpart)
// to this upstream user. .await
warn!(username = %localpart, user_id = %existing_user.id, "Localpart template returned an existing username"); .map_err(RouteError::HomeserverConnection)?;
if maybe_existing_user.is_some() || !is_available {
if let Some(existing_user) = maybe_existing_user {
// The mapper returned a username which already exists, but isn't
// linked to this upstream user.
warn!(username = %localpart, user_id = %existing_user.id, "Localpart template returned an existing username");
}
// TODO: translate // TODO: translate
let ctx = ErrorContext::new() let ctx = ErrorContext::new()
@@ -476,6 +488,7 @@ pub(crate) async fn post(
mut policy: Policy, mut policy: Policy,
PreferredLanguage(locale): PreferredLanguage, PreferredLanguage(locale): PreferredLanguage,
State(templates): State<Templates>, State(templates): State<Templates>,
State(homeserver): State<BoxHomeserverConnection>,
State(url_builder): State<UrlBuilder>, State(url_builder): State<UrlBuilder>,
State(site_config): State<SiteConfig>, State(site_config): State<SiteConfig>,
Path(link_id): Path<Ulid>, Path(link_id): Path<Ulid>,
@@ -682,7 +695,14 @@ pub(crate) async fn post(
// Check if there is an existing user // Check if there is an existing user
let existing_user = repo.user().find_by_username(&username).await?; let existing_user = repo.user().find_by_username(&username).await?;
if let Some(_existing_user) = existing_user {
// Ask the homeserver to make sure the username is valid
let is_available = homeserver
.is_localpart_available(&username)
.await
.map_err(RouteError::HomeserverConnection)?;
if existing_user.is_some() || !is_available {
// If there is an existing user, we can't create a new one // If there is an existing user, we can't create a new one
// with the same username, show an error // with the same username, show an error

View File

@@ -28,6 +28,7 @@ use mas_axum_utils::{
}; };
use mas_data_model::UserAgent; use mas_data_model::UserAgent;
use mas_i18n::DataLocale; use mas_i18n::DataLocale;
use mas_matrix::BoxHomeserverConnection;
use mas_policy::Policy; use mas_policy::Policy;
use mas_router::UrlBuilder; use mas_router::UrlBuilder;
use mas_storage::{ use mas_storage::{
@@ -111,6 +112,7 @@ pub(crate) async fn post(
State(templates): State<Templates>, State(templates): State<Templates>,
State(url_builder): State<UrlBuilder>, State(url_builder): State<UrlBuilder>,
State(site_config): State<SiteConfig>, State(site_config): State<SiteConfig>,
State(homeserver): State<BoxHomeserverConnection>,
mut policy: Policy, mut policy: Policy,
mut repo: BoxRepository, mut repo: BoxRepository,
activity_tracker: BoundActivityTracker, activity_tracker: BoundActivityTracker,
@@ -135,6 +137,16 @@ pub(crate) async fn post(
if form.username.is_empty() { if form.username.is_empty() {
state.add_error_on_field(RegisterFormField::Username, FieldError::Required); state.add_error_on_field(RegisterFormField::Username, FieldError::Required);
} else if repo.user().exists(&form.username).await? { } else if repo.user().exists(&form.username).await? {
// The user already exists in the database
state.add_error_on_field(RegisterFormField::Username, FieldError::Exists);
} else if !homeserver.is_localpart_available(&form.username).await? {
// The user already exists on the homeserver
// XXX: we may want to return different errors like "this username is reserved"
tracing::warn!(
username = &form.username,
"User tried to register with a reserved username"
);
state.add_error_on_field(RegisterFormField::Username, FieldError::Exists); state.add_error_on_field(RegisterFormField::Username, FieldError::Exists);
} }