2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								< ? php  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								namespace  Tests\Auth ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								use  BookStack\Auth\Access\LoginService ;  
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								use  BookStack\Auth\Access\Mfa\MfaValue ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								use  BookStack\Auth\Access\Mfa\TotpService ;  
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								use  BookStack\Auth\Role ;  
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								use  BookStack\Auth\User ;  
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								use  BookStack\Exceptions\StoppedAuthenticationException ;  
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								use  Illuminate\Support\Facades\Hash ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								use  PragmaRX\Google2FA\Google2FA ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								use  Tests\TestCase ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								use  Tests\TestResponse ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								class  MfaVerificationTest  extends  TestCase  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								{  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  function  test_totp_verification () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        [ $user ,  $secret ,  $loginResp ]  =  $this -> startTotpLogin (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $loginResp -> assertRedirect ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSee ( 'Verify Access' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSee ( 'Enter the code, generated using your mobile app, below:' ); 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-23 22:10:18 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        $this -> withHtml ( $resp ) -> assertElementExists ( 'form[action$="/mfa/totp/verify"] input[name="code"][autofocus]' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $google2fa  =  new  Google2FA (); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/totp/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  $google2fa -> getCurrentOtp ( $secret ), 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertRedirect ( '/' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertEquals ( $user -> id ,  auth () -> user () -> id ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  function  test_totp_verification_fails_on_missing_invalid_code () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        [ $user ,  $secret ,  $loginResp ]  =  $this -> startTotpLogin (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/totp/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  '' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertRedirect ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'The code field is required.' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertNull ( auth () -> user ()); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/totp/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  '123321' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertRedirect ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'The provided code is not valid or has expired.' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertNull ( auth () -> user ()); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    public  function  test_backup_code_verification () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        [ $user ,  $codes ,  $loginResp ]  =  $this -> startBackupCodeLogin (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $loginResp -> assertRedirect ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSee ( 'Verify Access' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSee ( 'Backup Code' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSee ( 'Enter one of your remaining backup codes below:' ); 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-23 22:10:18 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $this -> withHtml ( $resp ) -> assertElementExists ( 'form[action$="/mfa/backup_codes/verify"] input[name="code"]' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/backup_codes/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  $codes [ 1 ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertRedirect ( '/' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertEquals ( $user -> id ,  auth () -> user () -> id ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Ensure code no longer exists in available set
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $userCodes  =  MfaValue :: getValueForUser ( $user ,  MfaValue :: METHOD_BACKUP_CODES ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertStringNotContainsString ( $codes [ 1 ],  $userCodes ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertStringContainsString ( $codes [ 0 ],  $userCodes ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  function  test_backup_code_verification_fails_on_missing_or_invalid_code () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        [ $user ,  $codes ,  $loginResp ]  =  $this -> startBackupCodeLogin (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/backup_codes/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  '' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertRedirect ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'The code field is required.' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertNull ( auth () -> user ()); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/backup_codes/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  'ab123-ab456' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertRedirect ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'The provided code is not valid or has already been used.' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertNull ( auth () -> user ()); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  function  test_backup_code_verification_fails_on_attempted_code_reuse () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        [ $user ,  $codes ,  $loginResp ]  =  $this -> startBackupCodeLogin (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $this -> post ( '/mfa/backup_codes/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  $codes [ 0 ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertNotNull ( auth () -> user ()); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        auth () -> logout (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        session () -> flush (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> post ( '/login' ,  [ 'email'  =>  $user -> email ,  'password'  =>  'password' ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/backup_codes/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  $codes [ 0 ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertRedirect ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertNull ( auth () -> user ()); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'The provided code is not valid or has already been used.' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  function  test_backup_code_verification_shows_warning_when_limited_codes_remain () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        [ $user ,  $codes ,  $loginResp ]  =  $this -> startBackupCodeLogin ([ 'abc12-def45' ,  'abc12-def46' ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/backup_codes/verify' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'code'  =>  $codes [ 0 ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> followRedirects ( $resp ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  function  test_both_mfa_options_available_if_set_on_profile () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user  =  $this -> getEditor (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user -> password  =  Hash :: make ( 'password' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user -> save (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        MfaValue :: upsertWithValue ( $user ,  MfaValue :: METHOD_TOTP ,  'abc123' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        MfaValue :: upsertWithValue ( $user ,  MfaValue :: METHOD_BACKUP_CODES ,  '["abc12-def456"]' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /** @var TestResponse $mfaView */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $mfaView  =  $this -> followingRedirects () -> post ( '/login' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:49:40 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            'email'     =>  $user -> email , 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'password'  =>  'password' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Totp shown by default
 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-23 22:10:18 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $this -> withHtml ( $mfaView ) -> assertElementExists ( 'form[action$="/mfa/totp/verify"] input[name="code"]' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> withHtml ( $mfaView ) -> assertElementContains ( 'a[href$="/mfa/verify?method=backup_codes"]' ,  'Verify using a backup code' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Ensure can view backup_codes view
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/mfa/verify?method=backup_codes' ); 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-23 22:10:18 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $this -> withHtml ( $resp ) -> assertElementExists ( 'form[action$="/mfa/backup_codes/verify"] input[name="code"]' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> withHtml ( $resp ) -> assertElementContains ( 'a[href$="/mfa/verify?method=totp"]' ,  'Verify using a mobile app' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    public  function  test_mfa_required_with_no_methods_leads_to_setup () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user  =  $this -> getEditor (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user -> password  =  Hash :: make ( 'password' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user -> save (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /** @var Role $role */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $role  =  $user -> roles -> first (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $role -> mfa_enforced  =  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $role -> save (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertDatabaseMissing ( 'mfa_values' ,  [ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            'user_id'  =>  $user -> id , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /** @var TestResponse $resp */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> followingRedirects () -> post ( '/login' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:49:40 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'email'     =>  $user -> email , 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'password'  =>  'password' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'No Methods Configured' ); 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-23 22:10:18 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $this -> withHtml ( $resp ) -> assertElementContains ( 'a[href$="/mfa/setup"]' ,  'Configure' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> get ( '/mfa/backup_codes/generate' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:14:24 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        $resp  =  $this -> post ( '/mfa/backup_codes/confirm' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertRedirect ( '/login' ); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $this -> assertDatabaseHas ( 'mfa_values' ,  [ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            'user_id'  =>  $user -> id , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:14:24 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> get ( '/login' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'Multi-factor method configured, Please now login again using the configured method.' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        $resp  =  $this -> followingRedirects () -> post ( '/login' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:49:40 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'email'     =>  $user -> email , 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'password'  =>  'password' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $resp -> assertSeeText ( 'Enter one of your remaining backup codes below:' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    public  function  test_mfa_setup_route_access () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $routes  =  [ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            [ 'get' ,  '/mfa/setup' ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            [ 'get' ,  '/mfa/totp/generate' ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            [ 'post' ,  '/mfa/totp/confirm' ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            [ 'get' ,  '/mfa/backup_codes/generate' ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            [ 'post' ,  '/mfa/backup_codes/confirm' ], 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Non-auth access
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        foreach  ( $routes  as  [ $method ,  $path ])  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            $resp  =  $this -> call ( $method ,  $path ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            $resp -> assertRedirect ( '/login' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Attempted login user, who has configured mfa, access
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Sets up user that has MFA required after attempted login.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $loginService  =  $this -> app -> make ( LoginService :: class ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user  =  $this -> getEditor (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        /** @var Role $role */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $role  =  $user -> roles -> first (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $role -> mfa_enforced  =  true ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $role -> save (); 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:49:40 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-03 05:02:25 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        try  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            $loginService -> login ( $user ,  'testing' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        }  catch  ( StoppedAuthenticationException  $e )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertNotNull ( $loginService -> getLastLoginAttemptUser ()); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        MfaValue :: upsertWithValue ( $user ,  MfaValue :: METHOD_BACKUP_CODES ,  '[]' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        foreach  ( $routes  as  [ $method ,  $path ])  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            $resp  =  $this -> call ( $method ,  $path ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            $resp -> assertRedirect ( '/login' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-05-31 01:31:08 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    public  function  test_login_mfa_interception_does_not_log_error () 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $logHandler  =  $this -> withTestLogger (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        [ $user ,  $secret ,  $loginResp ]  =  $this -> startTotpLogin (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $loginResp -> assertRedirect ( '/mfa/verify' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $this -> assertFalse ( $logHandler -> hasErrorRecords ()); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    /** 
							 
						 
					
						
							
								
									
										
										
										
											2021-11-01 21:26:02 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								     *  @ return  array < User ,  string ,  TestResponse > 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								     */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    protected  function  startTotpLogin () :  array 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $secret  =  $this -> app -> make ( TotpService :: class ) -> generateSecret (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user  =  $this -> getEditor (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user -> password  =  Hash :: make ( 'password' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user -> save (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        MfaValue :: upsertWithValue ( $user ,  MfaValue :: METHOD_TOTP ,  $secret ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $loginResp  =  $this -> post ( '/login' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:49:40 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'email'     =>  $user -> email , 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 22:04:43 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'password'  =>  'password' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        return  [ $user ,  $secret ,  $loginResp ]; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    /** 
							 
						 
					
						
							
								
									
										
										
										
											2021-11-01 21:26:02 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								     *  @ return  array < User ,  string ,  TestResponse > 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								     */ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:49:40 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    protected  function  startBackupCodeLogin ( $codes  =  [ 'kzzu6-1pgll' ,  'bzxnf-plygd' ,  'bwdsp-ysl51' ,  '1vo93-ioy7n' ,  'lf7nw-wdyka' ,  'xmtrd-oplac' ]) :  array 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user  =  $this -> getEditor (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user -> password  =  Hash :: make ( 'password' ); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $user -> save (); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        MfaValue :: upsertWithValue ( $user ,  MfaValue :: METHOD_BACKUP_CODES ,  json_encode ( $codes )); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        $loginResp  =  $this -> post ( '/login' ,  [ 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:49:40 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'email'     =>  $user -> email , 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-02 23:35:37 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								            'password'  =>  'password' , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        ]); 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        return  [ $user ,  $codes ,  $loginResp ]; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-21 22:49:40 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}