Skip to content

Commit 76ba8e1

Browse files
committed
Allow passing MXIDs in the login page
1 parent 0096076 commit 76ba8e1

File tree

1 file changed

+106
-10
lines changed

1 file changed

+106
-10
lines changed

crates/handlers/src/views/login.rs

Lines changed: 106 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,19 @@ pub(crate) async fn post(
168168
return Ok((cookie_jar, Html(content)).into_response());
169169
}
170170

171+
// Extract the localpart of the MXID, fallback to the bare username
172+
let username = homeserver
173+
.localpart(&form.username)
174+
.unwrap_or(&form.username);
175+
171176
match login(
172177
password_manager,
173178
&mut repo,
174179
rng,
175180
&clock,
176181
limiter,
177182
requester,
178-
&form.username,
183+
username,
179184
&form.password,
180185
user_agent,
181186
)
@@ -479,30 +484,34 @@ mod test {
479484
.contains(&escape_html(&second_provider_login.path_and_query())));
480485
}
481486

482-
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
483-
async fn test_password_login(pool: PgPool) {
484-
setup();
485-
let state = TestState::from_pool(pool).await.unwrap();
487+
async fn user_with_password(state: &TestState, username: &str, password: &str) {
486488
let mut rng = state.rng();
487-
let cookies = CookieHelper::new();
488-
489-
// Provision a user with a password
490489
let mut repo = state.repository().await.unwrap();
491490
let user = repo
492491
.user()
493-
.add(&mut rng, &state.clock, "john".to_owned())
492+
.add(&mut rng, &state.clock, username.to_owned())
494493
.await
495494
.unwrap();
496495
let (version, hash) = state
497496
.password_manager
498-
.hash(&mut rng, Zeroizing::new("hunter2".as_bytes().to_vec()))
497+
.hash(&mut rng, Zeroizing::new(password.as_bytes().to_vec()))
499498
.await
500499
.unwrap();
501500
repo.user_password()
502501
.add(&mut rng, &state.clock, &user, version, hash, None)
503502
.await
504503
.unwrap();
505504
repo.save().await.unwrap();
505+
}
506+
507+
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
508+
async fn test_password_login(pool: PgPool) {
509+
setup();
510+
let state = TestState::from_pool(pool).await.unwrap();
511+
let cookies = CookieHelper::new();
512+
513+
// Provision a user with a password
514+
user_with_password(&state, "john", "hunter2").await;
506515

507516
// Render the login page to get a CSRF token
508517
let request = Request::get("/login").empty();
@@ -542,6 +551,93 @@ mod test {
542551
assert!(response.body().contains("john"));
543552
}
544553

554+
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
555+
async fn test_password_login_with_mxid(pool: PgPool) {
556+
setup();
557+
let state = TestState::from_pool(pool).await.unwrap();
558+
let cookies = CookieHelper::new();
559+
560+
// Provision a user with a password
561+
user_with_password(&state, "john", "hunter2").await;
562+
563+
// Render the login page to get a CSRF token
564+
let request = Request::get("/login").empty();
565+
let request = cookies.with_cookies(request);
566+
let response = state.request(request).await;
567+
cookies.save_cookies(&response);
568+
response.assert_status(StatusCode::OK);
569+
response.assert_header_value(CONTENT_TYPE, "text/html; charset=utf-8");
570+
// Extract the CSRF token from the response body
571+
let csrf_token = response
572+
.body()
573+
.split("name=\"csrf\" value=\"")
574+
.nth(1)
575+
.unwrap()
576+
.split('\"')
577+
.next()
578+
.unwrap();
579+
580+
// Submit the login form
581+
let request = Request::post("/login").form(serde_json::json!({
582+
"csrf": csrf_token,
583+
"username": "@john:example.com",
584+
"password": "hunter2",
585+
}));
586+
let request = cookies.with_cookies(request);
587+
let response = state.request(request).await;
588+
cookies.save_cookies(&response);
589+
response.assert_status(StatusCode::SEE_OTHER);
590+
591+
// Now if we get to the home page, we should see the user's username
592+
let request = Request::get("/").empty();
593+
let request = cookies.with_cookies(request);
594+
let response = state.request(request).await;
595+
cookies.save_cookies(&response);
596+
response.assert_status(StatusCode::OK);
597+
response.assert_header_value(CONTENT_TYPE, "text/html; charset=utf-8");
598+
assert!(response.body().contains("john"));
599+
}
600+
601+
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
602+
async fn test_password_login_with_mxid_wrong_server(pool: PgPool) {
603+
setup();
604+
let state = TestState::from_pool(pool).await.unwrap();
605+
let cookies = CookieHelper::new();
606+
607+
// Provision a user with a password
608+
user_with_password(&state, "john", "hunter2").await;
609+
610+
// Render the login page to get a CSRF token
611+
let request = Request::get("/login").empty();
612+
let request = cookies.with_cookies(request);
613+
let response = state.request(request).await;
614+
cookies.save_cookies(&response);
615+
response.assert_status(StatusCode::OK);
616+
response.assert_header_value(CONTENT_TYPE, "text/html; charset=utf-8");
617+
// Extract the CSRF token from the response body
618+
let csrf_token = response
619+
.body()
620+
.split("name=\"csrf\" value=\"")
621+
.nth(1)
622+
.unwrap()
623+
.split('\"')
624+
.next()
625+
.unwrap();
626+
627+
// Submit the login form
628+
let request = Request::post("/login").form(serde_json::json!({
629+
"csrf": csrf_token,
630+
"username": "@john:something.corp",
631+
"password": "hunter2",
632+
}));
633+
let request = cookies.with_cookies(request);
634+
let response = state.request(request).await;
635+
636+
// This shouldn't have worked, we're back on the login page
637+
response.assert_status(StatusCode::OK);
638+
assert!(response.body().contains("Invalid credentials"));
639+
}
640+
545641
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
546642
async fn test_password_login_rate_limit(pool: PgPool) {
547643
setup();

0 commit comments

Comments
 (0)