Skip to content

Commit d9d4243

Browse files
committed
Let admin API add users synchronously
as opposed to always launching an asynchronous worker job. This allows callers to have a guarantee that the user is fully created by the time it receives the response to the user creation request.
1 parent 872d725 commit d9d4243

File tree

1 file changed

+41
-4
lines changed
  • crates/handlers/src/admin/v1/users

1 file changed

+41
-4
lines changed

crates/handlers/src/admin/v1/users/add.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use aide::{NoApi, OperationIo, transform::TransformOperation};
1010
use axum::{Json, extract::State, response::IntoResponse};
1111
use hyper::StatusCode;
1212
use mas_axum_utils::record_error;
13-
use mas_matrix::HomeserverConnection;
13+
use mas_matrix::{HomeserverConnection, ProvisionRequest};
1414
use mas_storage::{
1515
BoxRng,
1616
queue::{ProvisionUserJob, QueueJobRepositoryExt as _},
@@ -106,6 +106,10 @@ pub struct Request {
106106
/// tokens (like with admin access) for them
107107
#[serde(default)]
108108
skip_homeserver_check: bool,
109+
110+
/// Delay the response until the user has been created on the homeserver.
111+
#[serde(default)]
112+
add_synchronously: bool,
109113
}
110114

111115
pub fn doc(operation: TransformOperation) -> TransformOperation {
@@ -168,9 +172,19 @@ pub async fn handler(
168172

169173
let user = repo.user().add(&mut rng, &clock, params.username).await?;
170174

171-
repo.queue_job()
172-
.schedule_job(&mut rng, &clock, ProvisionUserJob::new(&user))
173-
.await?;
175+
if params.add_synchronously {
176+
homeserver
177+
.provision_user(&ProvisionRequest::new(
178+
homeserver.mxid(&user.username),
179+
&user.sub,
180+
))
181+
.await
182+
.map_err(RouteError::Homeserver)?;
183+
} else {
184+
repo.queue_job()
185+
.schedule_job(&mut rng, &clock, ProvisionUserJob::new(&user))
186+
.await?;
187+
}
174188

175189
repo.save().await?;
176190

@@ -183,6 +197,7 @@ pub async fn handler(
183197
#[cfg(test)]
184198
mod tests {
185199
use hyper::{Request, StatusCode};
200+
use mas_matrix::HomeserverConnection;
186201
use mas_storage::{RepositoryAccess, user::UserRepository};
187202
use sqlx::PgPool;
188203

@@ -220,6 +235,28 @@ mod tests {
220235
assert_eq!(user.username, "alice");
221236
}
222237

238+
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
239+
async fn test_add_user_synchronously(pool: PgPool) {
240+
setup();
241+
let mut state = TestState::from_pool(pool).await.unwrap();
242+
let token = state.token_with_scope("urn:mas:admin").await;
243+
244+
let request = Request::post("/api/admin/v1/users")
245+
.bearer(&token)
246+
.json(serde_json::json!({
247+
"username": "alice",
248+
"add_synchronously": true,
249+
}));
250+
251+
let response = state.request(request).await;
252+
response.assert_status(StatusCode::CREATED);
253+
254+
// Check that the user was created on the homeserver
255+
let mxid = state.homeserver_connection.mxid("alice");
256+
let result = state.homeserver_connection.query_user(&mxid).await;
257+
assert!(result.is_ok());
258+
}
259+
223260
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
224261
async fn test_add_user_invalid_username(pool: PgPool) {
225262
setup();

0 commit comments

Comments
 (0)