Skip to content

Commit a4ce77f

Browse files
authored
Update error codes for delegation endpoint (#229)
1 parent cbde6a9 commit a4ce77f

5 files changed

Lines changed: 121 additions & 48 deletions

File tree

integration-tests/src/fake_homeserver.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ struct HsState {
7474
/// The recorded /delayed_events requests.
7575
delayed_event_requests: Vec<DelayedEventRequest>,
7676

77-
/// The delay (in ms) `GET /delayed_events/{delay_id}` reports, keyed by
78-
/// delay ID.
79-
delays: HashMap<String, i64>,
77+
/// The delay in ms and room_id that `GET /delayed_events/{delay_id}` reports,
78+
/// keyed by delay ID.
79+
delays: HashMap<String, (i64, String)>,
8080

8181
/// The HTTP status to return on delayed-event look-ups, overriding the
8282
/// scripted delays.
@@ -237,14 +237,14 @@ impl FakeHomeserver {
237237
self.state.lock().unwrap().delayed_event_requests.clone()
238238
}
239239

240-
/// Makes `GET /delayed_events/{delay_id}` report the given delay for
241-
/// `delay_id`.
242-
pub fn set_delay(&self, delay_id: &str, delay_ms: i64) {
240+
/// Makes `GET /delayed_events/{delay_id}` report the given delay and
241+
/// room ID for `delay_id`.
242+
pub fn set_delay(&self, delay_id: &str, delay_ms: i64, room_id: &str) {
243243
self.state
244244
.lock()
245245
.unwrap()
246246
.delays
247-
.insert(delay_id.to_owned(), delay_ms);
247+
.insert(delay_id.to_owned(), (delay_ms, room_id.to_owned()));
248248
}
249249

250250
/// Sets the HTTP status delayed-event look-ups fail with, regardless of the
@@ -375,11 +375,11 @@ async fn handle_delayed_event_look_up(
375375
}
376376

377377
match state.delays.get(&delay_id) {
378-
Some(delay_ms) => (
378+
Some((delay_ms, room_id)) => (
379379
StatusCode::OK,
380380
Json(json!({
381381
"delay_id": delay_id,
382-
"room_id": "!room:example.com",
382+
"room_id": room_id,
383383
"type": "m.room.member",
384384
"delay_ms": delay_ms,
385385
"content": {},

integration-tests/tests/delegate_delayed_leave_cs.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ async fn restart_and_send_use_identity_assertion() {
330330
async fn delay_timeout_looked_up_when_absent() {
331331
let hs = FakeHomeserver::new().await;
332332
let user = hs.new_user("alice");
333-
hs.set_delay("syd_cs_integration_1", 8000);
333+
hs.set_delay("syd_cs_integration_1", 8000, "!room:example.com");
334334

335335
let redis = FakeRedis::new().await;
336336

@@ -401,7 +401,7 @@ async fn delay_timeout_not_looked_up_when_given() {
401401
async fn looked_up_delay_drives_the_job() {
402402
let hs = FakeHomeserver::new().await;
403403
let user = hs.new_user("alice");
404-
hs.set_delay("syd_cs_integration_1", 300);
404+
hs.set_delay("syd_cs_integration_1", 300, "!room:example.com");
405405

406406
let svc = Service::start(ServiceConfig {
407407
full_access_homeservers: vec!["*".to_owned()],
@@ -420,8 +420,8 @@ async fn looked_up_delay_drives_the_job() {
420420
.await;
421421
}
422422

423-
/// An unknown delay ID is rejected with 404 M_NOT_FOUND, and no job is
424-
/// scheduled for it.
423+
/// An unknown delay ID is rejected with 400 M_INVALID_PARAM per MSC4195, and
424+
/// no job is scheduled for it.
425425
#[tokio::test]
426426
async fn unknown_delay_id_rejected() {
427427
let hs = FakeHomeserver::new().await;
@@ -440,7 +440,31 @@ async fn unknown_delay_id_rejected() {
440440
request.as_object_mut().unwrap().remove("delay_timeout");
441441
let (status, body) = post_delegate_cs(&svc, request.to_string(), Some(&user.user_id)).await;
442442

443-
expect_matrix_error(status, &body, 404, "M_NOT_FOUND");
443+
expect_matrix_error(status, &body, 400, "M_INVALID_PARAM");
444+
expect_no_delayed_event_requests(&hs);
445+
}
446+
447+
/// A delay ID that exists but was scheduled for a different room than the
448+
/// one being delegated for is rejected.
449+
#[tokio::test]
450+
async fn delay_id_for_another_room_rejected() {
451+
let hs = FakeHomeserver::new().await;
452+
let user = hs.new_user("alice");
453+
hs.set_delay("syd_cs_integration_1", 300, "!other-room:example.com");
454+
455+
let svc = Service::start(ServiceConfig {
456+
full_access_homeservers: vec!["*".to_owned()],
457+
cs_api_url_overrides: hs.cs_api_url_override(),
458+
extra_env: app_service_env_with_hs_server_name(hs.server_name()),
459+
..Default::default()
460+
})
461+
.await;
462+
463+
let mut request = delegate_request();
464+
request.as_object_mut().unwrap().remove("delay_timeout");
465+
let (status, body) = post_delegate_cs(&svc, request.to_string(), Some(&user.user_id)).await;
466+
467+
expect_matrix_error(status, &body, 400, "M_INVALID_PARAM");
444468
expect_no_delayed_event_requests(&hs);
445469
}
446470

@@ -450,7 +474,7 @@ async fn unknown_delay_id_rejected() {
450474
async fn delay_lookup_failure_rejected() {
451475
let hs = FakeHomeserver::new().await;
452476
let user = hs.new_user("alice");
453-
hs.set_delay("syd_cs_integration_1", 8000);
477+
hs.set_delay("syd_cs_integration_1", 8000, "!room:example.com");
454478
hs.set_delay_lookup_status(500);
455479

456480
let svc = Service::start(ServiceConfig {

src/handler.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -718,17 +718,20 @@ impl Handler {
718718
}
719719

720720
/// Looks up the delay of `delay_id` on the homeserver, for delegation
721-
/// requests that do not carry one.
721+
/// requests that do not carry one. Also verifies that the delayed event
722+
/// was scheduled in `room_id`.
722723
async fn look_up_delay_timeout(
723724
&self,
724725
cs_api_url: &CsApiUrl,
725726
delay_id: &str,
727+
room_id: &str,
726728
owner_user_id: &str,
727729
) -> Result<Duration, MatrixErrorResponse> {
728730
self.deps
729731
.get_delayed_event_delay(
730732
cs_api_url,
731733
delay_id,
734+
room_id,
732735
AppServiceIdentity {
733736
as_token: &self.app_service_config.as_token,
734737
user_id: owner_user_id,
@@ -740,8 +743,8 @@ impl Handler {
740743
"Handler: could not look up the delay of the delayed event");
741744
if err.is_delayed_event_not_found() {
742745
MatrixErrorResponse {
743-
status: 404,
744-
errcode: "M_NOT_FOUND".into(),
746+
status: 400,
747+
errcode: "M_INVALID_PARAM".into(),
745748
err: "Unknown `delay_id`".into(),
746749
}
747750
} else {
@@ -1240,7 +1243,7 @@ impl Handler {
12401243
let delay_timeout = match req.delay_timeout {
12411244
Some(timeout) => Duration::from_millis(timeout.max(0) as u64),
12421245
None => {
1243-
self.look_up_delay_timeout(&cs_api_url, &req.delay_id, mxid_header)
1246+
self.look_up_delay_timeout(&cs_api_url, &req.delay_id, &req.room_id, mxid_header)
12441247
.await?
12451248
}
12461249
};

src/handler_tests.rs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type ExecuteDelayedEventActionFn = Box<
3939
dyn Fn(&CsApiUrl, &str, DelayEventAction, &str, &str) -> Result<u16, ActionError> + Send + Sync,
4040
>;
4141
type GetDelayedEventDelayFn =
42-
Box<dyn Fn(&CsApiUrl, &str, &str, &str) -> Result<Duration, ActionError> + Send + Sync>;
42+
Box<dyn Fn(&CsApiUrl, &str, &str, &str, &str) -> Result<Duration, ActionError> + Send + Sync>;
4343
type IsJoinedFn = Box<dyn Fn(&CsApiUrl, &str, &str) -> Result<bool, String> + Send + Sync>;
4444
type RequestGetTokenViaFederationFn = Box<
4545
dyn Fn(&CsApiUrl, &str, &GetTokenSsRequest) -> Result<GetTokenSsResponse, FederationTokenError>
@@ -143,10 +143,17 @@ impl Deps for HandlerTestDeps {
143143
&self,
144144
cs_api_url: &CsApiUrl,
145145
delay_id: &str,
146+
room_id: &str,
146147
identity: AppServiceIdentity<'_>,
147148
) -> Result<Duration, ActionError> {
148149
match &self.get_delayed_event_delay_fn {
149-
Some(f) => f(cs_api_url, delay_id, identity.as_token, identity.user_id),
150+
Some(f) => f(
151+
cs_api_url,
152+
delay_id,
153+
room_id,
154+
identity.as_token,
155+
identity.user_id,
156+
),
150157
None => panic!("get_delayed_event_delay not mocked in HandlerTestDeps"),
151158
}
152159
}
@@ -4182,11 +4189,12 @@ async fn test_process_delegate_delayed_leave_cs_restart_uses_identity_assertion(
41824189
}
41834190

41844191
/// A request without a delay timeout makes the service look the delay up on
4185-
/// the homeserver, asserting the caller's identity as it does so.
4192+
/// the homeserver, asserting the caller's identity and the request's
4193+
/// `room_id` as it does so.
41864194
#[tokio::test]
41874195
async fn test_process_delegate_delayed_leave_cs_looks_up_missing_delay_timeout() {
4188-
/// (CS API URL, delay ID, as_token, user_id) of the recorded lookup.
4189-
type Lookup = (String, String, String, String);
4196+
/// (CS API URL, delay ID, room ID, as_token, user_id) of the recorded lookup.
4197+
type Lookup = (String, String, String, String, String);
41904198
let looked_up: Arc<Mutex<Option<Lookup>>> = Arc::new(Mutex::new(None));
41914199
let looked_up_clone = looked_up.clone();
41924200
let deps = HandlerTestDeps {
@@ -4195,10 +4203,11 @@ async fn test_process_delegate_delayed_leave_cs_looks_up_missing_delay_timeout()
41954203
})),
41964204
participant_exists_fn: participant_exists_block_until_cancelled(),
41974205
get_delayed_event_delay_fn: Some(Box::new(
4198-
move |cs_api_url, delay_id, as_token, user_id| {
4206+
move |cs_api_url, delay_id, room_id, as_token, user_id| {
41994207
*looked_up_clone.lock().unwrap() = Some((
42004208
cs_api_url.0.clone(),
42014209
delay_id.to_owned(),
4210+
room_id.to_owned(),
42024211
as_token.to_owned(),
42034212
user_id.to_owned(),
42044213
));
@@ -4216,13 +4225,14 @@ async fn test_process_delegate_delayed_leave_cs_looks_up_missing_delay_timeout()
42164225
.await
42174226
.expect("unexpected error");
42184227

4219-
let (cs_api_url, delay_id, as_token, user_id) = looked_up
4228+
let (cs_api_url, delay_id, room_id, as_token, user_id) = looked_up
42204229
.lock()
42214230
.unwrap()
42224231
.clone()
42234232
.expect("expected the delay to be looked up");
42244233
assert_eq!(cs_api_url, "https://matrix-client.example.com");
42254234
assert_eq!(delay_id, req.delay_id, "expected the requested delay_id");
4235+
assert_eq!(room_id, req.room_id, "expected the requested room_id");
42264236
assert_eq!(as_token, "as_token", "expected the configured as_token");
42274237
assert_eq!(
42284238
user_id, DELEGATE_DELAYED_LEAVE_CS_MXID,
@@ -4245,7 +4255,7 @@ async fn test_process_delegate_delayed_leave_cs_looked_up_delay_drives_the_job()
42454255
// Confirmed absent, so the job stays in WaitingForInitialConnect until
42464256
// its timeout — which is the looked-up delay — elapses.
42474257
participant_exists_fn: Some(Box::new(|_, _| Box::pin(async { Ok(false) }))),
4248-
get_delayed_event_delay_fn: Some(Box::new(|_, _, _, _| Ok(Duration::from_millis(200)))),
4258+
get_delayed_event_delay_fn: Some(Box::new(|_, _, _, _, _| Ok(Duration::from_millis(200)))),
42494259
execute_delayed_event_action_fn: Some(Box::new(move |_, _, action, _, _| {
42504260
if action == DelayEventAction::Send {
42514261
*sent_clone.lock().unwrap() = true;
@@ -4282,7 +4292,7 @@ async fn test_process_delegate_delayed_leave_cs_skips_lookup_when_delay_timeout_
42824292
Ok(CsApiUrl("https://matrix-client.example.com".into()))
42834293
})),
42844294
participant_exists_fn: participant_exists_block_until_cancelled(),
4285-
get_delayed_event_delay_fn: Some(Box::new(|_, _, _, _| {
4295+
get_delayed_event_delay_fn: Some(Box::new(|_, _, _, _, _| {
42864296
panic!("the delay must not be looked up when the request carries one")
42874297
})),
42884298
..Default::default()
@@ -4298,14 +4308,14 @@ async fn test_process_delegate_delayed_leave_cs_skips_lookup_when_delay_timeout_
42984308
handler.close().await;
42994309
}
43004310

4301-
/// An unknown delay_id surfaces as 404 M_NOT_FOUND.
4311+
/// An unknown delay_id is rejected as HTTP 400 / M_INVALID_PARAM.
43024312
#[tokio::test]
43034313
async fn test_process_delegate_delayed_leave_cs_delay_lookup_not_found() {
43044314
let deps = HandlerTestDeps {
43054315
resolve_cs_api_url_fn: Some(Box::new(|_| {
43064316
Ok(CsApiUrl("https://matrix-client.example.com".into()))
43074317
})),
4308-
get_delayed_event_delay_fn: Some(Box::new(|_, _, _, _| {
4318+
get_delayed_event_delay_fn: Some(Box::new(|_, _, _, _, _| {
43094319
Err(ActionError::DelayedEventNotFound { status: 404 })
43104320
})),
43114321
..Default::default()
@@ -4318,8 +4328,8 @@ async fn test_process_delegate_delayed_leave_cs_delay_lookup_not_found() {
43184328
.process_delegate_delayed_leave_cs(&req, DELEGATE_DELAYED_LEAVE_CS_MXID)
43194329
.await
43204330
.expect_err("expected MatrixErrorResponse");
4321-
assert_eq!(err.status, 404, "expected 404");
4322-
assert_eq!(err.errcode, "M_NOT_FOUND", "expected M_NOT_FOUND");
4331+
assert_eq!(err.status, 400, "expected 400");
4332+
assert_eq!(err.errcode, "M_INVALID_PARAM", "expected M_INVALID_PARAM");
43234333
handler.close().await;
43244334
}
43254335

@@ -4331,7 +4341,7 @@ async fn test_process_delegate_delayed_leave_cs_delay_lookup_unavailable() {
43314341
resolve_cs_api_url_fn: Some(Box::new(|_| {
43324342
Ok(CsApiUrl("https://matrix-client.example.com".into()))
43334343
})),
4334-
get_delayed_event_delay_fn: Some(Box::new(|_, _, _, _| {
4344+
get_delayed_event_delay_fn: Some(Box::new(|_, _, _, _, _| {
43354345
Err(ActionError::Transient {
43364346
status: 500,
43374347
msg: "boom".into(),

0 commit comments

Comments
 (0)