1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-09 04:22:45 +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( #[tracing::instrument(
name = "homeserver.provision_user", name = "homeserver.provision_user",
skip_all, skip_all,

View File

@@ -219,6 +219,17 @@ pub trait HomeserverConnection: Send + Sync {
/// be provisioned. /// be provisioned.
async fn provision_user(&self, request: &ProvisionRequest) -> Result<bool, Self::Error>; 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. /// Create a device for a user on the homeserver.
/// ///
/// # Parameters /// # Parameters
@@ -312,6 +323,10 @@ impl<T: HomeserverConnection + Send + Sync + ?Sized> HomeserverConnection for &T
(**self).provision_user(request).await (**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> { async fn create_device(&self, mxid: &str, device_id: &str) -> Result<(), Self::Error> {
(**self).create_device(mxid, device_id).await (**self).create_device(mxid, device_id).await
} }

View File

@@ -34,6 +34,7 @@ struct MockUser {
pub struct HomeserverConnection { pub struct HomeserverConnection {
homeserver: String, homeserver: String,
users: RwLock<HashMap<String, MockUser>>, users: RwLock<HashMap<String, MockUser>>,
reserved_localparts: RwLock<HashSet<&'static str>>,
} }
impl HomeserverConnection { impl HomeserverConnection {
@@ -45,8 +46,13 @@ impl HomeserverConnection {
Self { Self {
homeserver: homeserver.into(), homeserver: homeserver.into(),
users: RwLock::new(HashMap::new()), 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] #[async_trait]
@@ -98,6 +104,16 @@ impl crate::HomeserverConnection for HomeserverConnection {
Ok(inserted) 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> { async fn create_device(&self, mxid: &str, device_id: &str) -> Result<(), Self::Error> {
let mut users = self.users.write().await; let mut users = self.users.write().await;
let user = users.get_mut(mxid).context("User not found")?; 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 // XXX: there is no API to query devices yet in the trait
// Delete the device // Delete the device
assert!(conn.delete_device(mxid, device).await.is_ok()); 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());
} }
} }