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

Add API to check localpart availability

This commit is contained in:
Quentin Gliech
2024-02-27 18:03:33 +01:00
parent 68c4bfaa11
commit 20dd5ca311
3 changed files with 71 additions and 0 deletions

View File

@@ -175,6 +175,37 @@ impl HomeserverConnection for SynapseConnection {
})
}
#[tracing::instrument(
name = "homeserver.is_localpart_available",
skip_all,
fields(
matrix.homeserver = self.homeserver,
matrix.localpart = localpart,
),
err(Display),
)]
async fn is_localpart_available(&self, localpart: &str) -> Result<bool, Self::Error> {
let mut client = self
.http_client_factory
.client("homeserver.is_localpart_available");
let request = self
.get(&format!(
"_synapse/admin/v1/username_available?username={localpart}"
))
.body(EmptyBody::new())?;
let response = client.ready().await?.call(request).await?;
match response.status() {
StatusCode::OK => Ok(true),
StatusCode::BAD_REQUEST => Ok(false),
_ => Err(anyhow::anyhow!(
"Failed to query localpart availability from Synapse"
)),
}
}
#[tracing::instrument(
name = "homeserver.provision_user",
skip_all,

View File

@@ -219,6 +219,17 @@ pub trait HomeserverConnection: Send + Sync {
/// be provisioned.
async fn provision_user(&self, request: &ProvisionRequest) -> Result<bool, Self::Error>;
/// Check whether a given username is available on the homeserver.
///
/// # Parameters
///
/// * `localpart` - The localpart to check.
///
/// # Errors
///
/// Returns an error if the homeserver is unreachable.
async fn is_localpart_available(&self, localpart: &str) -> Result<bool, Self::Error>;
/// Create a device for a user on the homeserver.
///
/// # Parameters
@@ -312,6 +323,10 @@ impl<T: HomeserverConnection + Send + Sync + ?Sized> HomeserverConnection for &T
(**self).provision_user(request).await
}
async fn is_localpart_available(&self, localpart: &str) -> Result<bool, Self::Error> {
(**self).is_localpart_available(localpart).await
}
async fn create_device(&self, mxid: &str, device_id: &str) -> Result<(), Self::Error> {
(**self).create_device(mxid, device_id).await
}

View File

@@ -34,6 +34,7 @@ struct MockUser {
pub struct HomeserverConnection {
homeserver: String,
users: RwLock<HashMap<String, MockUser>>,
reserved_localparts: RwLock<HashSet<&'static str>>,
}
impl HomeserverConnection {
@@ -45,8 +46,13 @@ impl HomeserverConnection {
Self {
homeserver: homeserver.into(),
users: RwLock::new(HashMap::new()),
reserved_localparts: RwLock::new(HashSet::new()),
}
}
pub async fn reserve_localpart(&self, localpart: &'static str) {
self.reserved_localparts.write().await.insert(localpart);
}
}
#[async_trait]
@@ -98,6 +104,16 @@ impl crate::HomeserverConnection for HomeserverConnection {
Ok(inserted)
}
async fn is_localpart_available(&self, localpart: &str) -> Result<bool, Self::Error> {
if self.reserved_localparts.read().await.contains(localpart) {
return Ok(false);
}
let mxid = self.mxid(localpart);
let users = self.users.read().await;
Ok(!users.contains_key(&mxid))
}
async fn create_device(&self, mxid: &str, device_id: &str) -> Result<(), Self::Error> {
let mut users = self.users.write().await;
let user = users.get_mut(mxid).context("User not found")?;
@@ -200,5 +216,14 @@ mod tests {
// XXX: there is no API to query devices yet in the trait
// Delete the device
assert!(conn.delete_device(mxid, device).await.is_ok());
// The user we just created should be not available
assert!(!conn.is_localpart_available("test").await.unwrap());
// But another user should be
assert!(conn.is_localpart_available("alice").await.unwrap());
// Reserve the localpart, it should not be available anymore
conn.reserve_localpart("alice").await;
assert!(!conn.is_localpart_available("alice").await.unwrap());
}
}