* Fix #3910 - Require OTP authentication to disable 2FA. Also, remove ability to generate new OTP backup codes *after* initial backup codes were handed out during activation * Restore recovery code re-generation * Improve display of some 2FA elements
This commit is contained in:
		
							parent
							
								
									ed7dc1704d
								
							
						
					
					
						commit
						5e8d037e27
					
				
					 27 changed files with 109 additions and 54 deletions
				
			
		|  | @ -7,7 +7,9 @@ module Settings | |||
|     before_action :authenticate_user! | ||||
|     before_action :verify_otp_required, only: [:create] | ||||
| 
 | ||||
|     def show; end | ||||
|     def show | ||||
|       @confirmation = Form::TwoFactorConfirmation.new | ||||
|     end | ||||
| 
 | ||||
|     def create | ||||
|       current_user.otp_secret = User.generate_otp_secret(32) | ||||
|  | @ -16,13 +18,23 @@ module Settings | |||
|     end | ||||
| 
 | ||||
|     def destroy | ||||
|       current_user.otp_required_for_login = false | ||||
|       current_user.save! | ||||
|       redirect_to settings_two_factor_authentication_path | ||||
|       if current_user.validate_and_consume_otp!(confirmation_params[:code]) | ||||
|         current_user.otp_required_for_login = false | ||||
|         current_user.save! | ||||
|         redirect_to settings_two_factor_authentication_path | ||||
|       else | ||||
|         flash.now[:alert] = I18n.t('two_factor_authentication.wrong_code') | ||||
|         @confirmation = Form::TwoFactorConfirmation.new | ||||
|         render :show | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     private | ||||
| 
 | ||||
|     def confirmation_params | ||||
|       params.require(:form_two_factor_confirmation).permit(:code) | ||||
|     end | ||||
| 
 | ||||
|     def verify_otp_required | ||||
|       redirect_to settings_two_factor_authentication_path if current_user.otp_required_for_login? | ||||
|     end | ||||
|  |  | |||
|  | @ -129,6 +129,11 @@ | |||
|         color: $ui-primary-color; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .positive-hint { | ||||
|       color: $valid-value-color; | ||||
|       font-weight: 500; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .simple_form { | ||||
|  |  | |||
|  | @ -358,7 +358,6 @@ code { | |||
| } | ||||
| 
 | ||||
| .user_filtered_languages { | ||||
| 
 | ||||
|   & > label { | ||||
|     font-family: inherit; | ||||
|     font-size: 16px; | ||||
|  |  | |||
|  | @ -10,7 +10,6 @@ | |||
| .recovery-codes { | ||||
|   list-style: none; | ||||
|   margin: 0 auto; | ||||
|   text-align: center; | ||||
| 
 | ||||
|   li { | ||||
|     font-size: 125%; | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| - content_for :page_title do | ||||
|   = t('settings.two_factor_authentication') | ||||
| 
 | ||||
| %p.hint= t('two_factor_authentication.recovery_instructions') | ||||
| %p.hint= t('two_factor_authentication.recovery_instructions_html') | ||||
| 
 | ||||
| %ol.recovery-codes | ||||
|   - @recovery_codes.each do |code| | ||||
|  |  | |||
|  | @ -1,26 +1,34 @@ | |||
| - content_for :page_title do | ||||
|   = t('settings.two_factor_authentication') | ||||
| 
 | ||||
| .simple_form | ||||
|   %p.hint | ||||
|     = t('two_factor_authentication.description_html') | ||||
| - if current_user.otp_required_for_login | ||||
|   %p.positive-hint | ||||
|     = fa_icon 'check' | ||||
|     = ' ' | ||||
|     = t 'two_factor_authentication.enabled' | ||||
| 
 | ||||
|   %hr/ | ||||
| 
 | ||||
|   = simple_form_for @confirmation, url: settings_two_factor_authentication_path, method: :delete do |f| | ||||
|     = f.input :code, hint: t('two_factor_authentication.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt') | ||||
| 
 | ||||
|     .actions | ||||
|       = f.button :button, t('two_factor_authentication.disable'), type: :submit | ||||
| 
 | ||||
|   %hr/ | ||||
| 
 | ||||
|   %h6= t('two_factor_authentication.recovery_codes') | ||||
|   %p.muted-hint | ||||
|     = t('two_factor_authentication.lost_recovery_codes') | ||||
|     = link_to t('two_factor_authentication.generate_recovery_codes'), | ||||
|       settings_two_factor_authentication_recovery_codes_path, | ||||
|       data: { method: :post } | ||||
| 
 | ||||
| - else | ||||
|   .simple_form | ||||
|     %p.hint= t('two_factor_authentication.description_html') | ||||
| 
 | ||||
|   - if current_user.otp_required_for_login | ||||
|     = link_to t('two_factor_authentication.disable'), | ||||
|       settings_two_factor_authentication_path, | ||||
|       data: { method: :delete }, | ||||
|       class: 'block-button' | ||||
|   - else | ||||
|     = link_to t('two_factor_authentication.setup'), | ||||
|       settings_two_factor_authentication_path, | ||||
|       data: { method: :post }, | ||||
|       class: 'block-button' | ||||
| 
 | ||||
| - if current_user.otp_required_for_login | ||||
|   .simple_form | ||||
|     %p.hint | ||||
|       = t('two_factor_authentication.lost_recovery_codes') | ||||
|     = link_to t('two_factor_authentication.generate_recovery_codes'), | ||||
|       settings_two_factor_authentication_recovery_codes_path, | ||||
|       data: { method: :post }, | ||||
|       class: 'block-button' | ||||
|  |  | |||
		Reference in a new issue