@@ -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