2FA controller cleanup (#2296)
* Add spec coverage for settings/two_factor_auth area * extract setup method for qr code * Move otp required check to before action * Merge method only used once * Remove duplicate view * Consolidate creation of @codes for backup * Move settings/2fq#recovery_codes to settings/recovery_codes#create * Rename settings/two_factor_auth#disable to #destroy * Add coverage for the otp required path on 2fa#show * Clean up the recovery codes list styles * Move settings/two_factor_auth to settings/two_factor_authentication * Reorganize the settings two factor auth area Updated to use a flow like: - settings/two_factor_authentication goes to a #show view which has a button either enable or disable 2fa on the account - the disable button turns off the otp requirement for the user - the enable button cycles the user secret and redirects to a confirmation page - the confirmation page is a #new view which shows the QR code for user - that page posts to #create which verifies the code, and creates the recovery codes - that create action shares a view with a recovery codes controller which can be used separately to reset codes if needed
This commit is contained in:
		| @@ -9,4 +9,12 @@ | ||||
|  | ||||
| .recovery-codes { | ||||
|   list-style: none; | ||||
|   margin: 0 auto; | ||||
|   text-align: center; | ||||
|  | ||||
|   li { | ||||
|     font-size: 125%; | ||||
|     line-height: 1.5; | ||||
|     letter-spacing: 1px; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,43 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| module Settings | ||||
|   module TwoFactorAuthentication | ||||
|     class ConfirmationsController < ApplicationController | ||||
|       layout 'admin' | ||||
|  | ||||
|       before_action :authenticate_user! | ||||
|  | ||||
|       def new | ||||
|         prepare_two_factor_form | ||||
|       end | ||||
|  | ||||
|       def create | ||||
|         if current_user.validate_and_consume_otp!(confirmation_params[:code]) | ||||
|           flash[:notice] = I18n.t('two_factor_authentication.enabled_success') | ||||
|  | ||||
|           current_user.otp_required_for_login = true | ||||
|           @recovery_codes = current_user.generate_otp_backup_codes! | ||||
|           current_user.save! | ||||
|  | ||||
|           render 'settings/two_factor_authentication/recovery_codes/index' | ||||
|         else | ||||
|           flash.now[:alert] = I18n.t('two_factor_authentication.wrong_code') | ||||
|           prepare_two_factor_form | ||||
|           render :new | ||||
|         end | ||||
|       end | ||||
|  | ||||
|       private | ||||
|  | ||||
|       def confirmation_params | ||||
|         params.require(:form_two_factor_confirmation).permit(:code) | ||||
|       end | ||||
|  | ||||
|       def prepare_two_factor_form | ||||
|         @confirmation = Form::TwoFactorConfirmation.new | ||||
|         @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain) | ||||
|         @qrcode = RQRCode::QRCode.new(@provision_url) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,18 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| module Settings | ||||
|   module TwoFactorAuthentication | ||||
|     class RecoveryCodesController < ApplicationController | ||||
|       layout 'admin' | ||||
|  | ||||
|       before_action :authenticate_user! | ||||
|  | ||||
|       def create | ||||
|         @recovery_codes = current_user.generate_otp_backup_codes! | ||||
|         current_user.save! | ||||
|         flash[:notice] = I18n.t('two_factor_authentication.recovery_codes_regenerated') | ||||
|         render :index | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,30 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| module Settings | ||||
|   class TwoFactorAuthenticationsController < ApplicationController | ||||
|     layout 'admin' | ||||
|  | ||||
|     before_action :authenticate_user! | ||||
|     before_action :verify_otp_required, only: [:create] | ||||
|  | ||||
|     def show; end | ||||
|  | ||||
|     def create | ||||
|       current_user.otp_secret = User.generate_otp_secret(32) | ||||
|       current_user.save! | ||||
|       redirect_to new_settings_two_factor_authentication_confirmation_path | ||||
|     end | ||||
|  | ||||
|     def destroy | ||||
|       current_user.otp_required_for_login = false | ||||
|       current_user.save! | ||||
|       redirect_to settings_two_factor_authentication_path | ||||
|     end | ||||
|  | ||||
|     private | ||||
|  | ||||
|     def verify_otp_required | ||||
|       redirect_to settings_two_factor_authentication_path if current_user.otp_required_for_login? | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -1,56 +0,0 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class Settings::TwoFactorAuthsController < ApplicationController | ||||
|   layout 'admin' | ||||
|  | ||||
|   before_action :authenticate_user! | ||||
|  | ||||
|   def show; end | ||||
|  | ||||
|   def new | ||||
|     redirect_to settings_two_factor_auth_path if current_user.otp_required_for_login | ||||
|  | ||||
|     @confirmation = Form::TwoFactorConfirmation.new | ||||
|     current_user.otp_secret = User.generate_otp_secret(32) | ||||
|     current_user.save! | ||||
|     set_qr_code | ||||
|   end | ||||
|  | ||||
|   def create | ||||
|     if current_user.validate_and_consume_otp!(confirmation_params[:code]) | ||||
|       current_user.otp_required_for_login = true | ||||
|       @codes = current_user.generate_otp_backup_codes! | ||||
|       current_user.save! | ||||
|       flash[:notice] = I18n.t('two_factor_auth.enabled_success') | ||||
|     else | ||||
|       @confirmation = Form::TwoFactorConfirmation.new | ||||
|       set_qr_code | ||||
|       flash.now[:alert] = I18n.t('two_factor_auth.wrong_code') | ||||
|       render :new | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def recovery_codes | ||||
|     @codes = current_user.generate_otp_backup_codes! | ||||
|     current_user.save! | ||||
|     flash[:notice] = I18n.t('two_factor_auth.recovery_codes_regenerated') | ||||
|   end | ||||
|  | ||||
|   def disable | ||||
|     current_user.otp_required_for_login = false | ||||
|     current_user.save! | ||||
|  | ||||
|     redirect_to settings_two_factor_auth_path | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def set_qr_code | ||||
|     @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain) | ||||
|     @qrcode        = RQRCode::QRCode.new(@provision_url) | ||||
|   end | ||||
|  | ||||
|   def confirmation_params | ||||
|     params.require(:form_two_factor_confirmation).permit(:code) | ||||
|   end | ||||
| end | ||||
| @@ -5,6 +5,6 @@ | ||||
|     %li= link_to t('settings.preferences'), settings_preferences_path | ||||
|   - if controller_name != 'registrations' | ||||
|     %li= link_to t('auth.change_password'), edit_user_registration_path | ||||
|   - if controller_name != 'two_factor_auths' | ||||
|     %li= link_to t('settings.two_factor_auth'), settings_two_factor_auth_path | ||||
|   - if controller_name != 'two_factor_authentications' | ||||
|     %li= link_to t('settings.two_factor_authentication'), settings_two_factor_authentication_path | ||||
|   %li= link_to t('settings.back'), root_path | ||||
|   | ||||
| @@ -0,0 +1,17 @@ | ||||
| - content_for :page_title do | ||||
|   = t('settings.two_factor_authentication') | ||||
|  | ||||
| = simple_form_for @confirmation, url: settings_two_factor_authentication_confirmation_path, method: :post do |f| | ||||
|   %p.hint= t('two_factor_authentication.instructions_html') | ||||
|  | ||||
|   .qr-wrapper | ||||
|     .qr-code= raw @qrcode.as_svg(padding: 0, module_size: 4) | ||||
|  | ||||
|     .qr-alternative | ||||
|       %p.hint= t('two_factor_authentication.manual_instructions') | ||||
|       %samp.qr-alternative__code= current_user.otp_secret.scan(/.{4}/).join(' ') | ||||
|  | ||||
|   = 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.enable'), type: :submit | ||||
| @@ -0,0 +1,9 @@ | ||||
| - content_for :page_title do | ||||
|   = t('settings.two_factor_authentication') | ||||
|  | ||||
| %p.hint= t('two_factor_authentication.recovery_instructions') | ||||
|  | ||||
| %ol.recovery-codes | ||||
|   - @recovery_codes.each do |code| | ||||
|     %li< | ||||
|       %samp= code | ||||
							
								
								
									
										26
									
								
								app/views/settings/two_factor_authentications/show.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/views/settings/two_factor_authentications/show.html.haml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| - 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 | ||||
|     = 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' | ||||
| @@ -1,6 +0,0 @@ | ||||
| %p.hint= t('two_factor_auth.recovery_instructions') | ||||
|  | ||||
| %ol.recovery-codes | ||||
|   - recovery_codes.each do |code| | ||||
|     %li | ||||
|       %samp= code | ||||
| @@ -1,4 +0,0 @@ | ||||
| - content_for :page_title do | ||||
|   = t('settings.two_factor_auth') | ||||
|  | ||||
| = render 'recovery_codes', recovery_codes: @codes | ||||
| @@ -1,17 +0,0 @@ | ||||
| - content_for :page_title do | ||||
|   = t('settings.two_factor_auth') | ||||
|  | ||||
| = simple_form_for @confirmation, url: settings_two_factor_auth_path, method: :post do |f| | ||||
|   %p.hint= t('two_factor_auth.instructions_html') | ||||
|  | ||||
|   .qr-wrapper | ||||
|     .qr-code= raw @qrcode.as_svg(padding: 0, module_size: 4) | ||||
|  | ||||
|     .qr-alternative | ||||
|       %p.hint= t('two_factor_auth.manual_instructions') | ||||
|       %samp.qr-alternative__code= current_user.otp_secret.scan(/.{4}/).join(' ') | ||||
|  | ||||
|   = f.input :code, hint: t('two_factor_auth.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt') | ||||
|  | ||||
|   .actions | ||||
|     = f.button :button, t('two_factor_auth.enable'), type: :submit | ||||
| @@ -1,4 +0,0 @@ | ||||
| - content_for :page_title do | ||||
|   = t('settings.two_factor_auth') | ||||
|  | ||||
| = render 'recovery_codes', recovery_codes: @codes | ||||
| @@ -1,17 +0,0 @@ | ||||
| - content_for :page_title do | ||||
|   = t('settings.two_factor_auth') | ||||
|  | ||||
| .simple_form | ||||
|   %p.hint= t('two_factor_auth.description_html') | ||||
|  | ||||
|   - if current_user.otp_required_for_login | ||||
|     = link_to t('two_factor_auth.disable'), disable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button' | ||||
|   - else | ||||
|     = link_to t('two_factor_auth.setup'), new_settings_two_factor_auth_path, class: 'block-button' | ||||
|  | ||||
| - if current_user.otp_required_for_login | ||||
|   %p | ||||
|  | ||||
|   .simple_form | ||||
|     %p.hint= t('two_factor_auth.lost_recovery_codes') | ||||
|     = link_to t('two_factor_auth.generate_recovery_codes'), recovery_codes_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button' | ||||
| @@ -139,7 +139,7 @@ bg: | ||||
|     import: Импортиране | ||||
|     preferences: Предпочитания | ||||
|     settings: Настройки | ||||
|     two_factor_auth: Двустепенно удостоверяване | ||||
|     two_factor_authentication: Двустепенно удостоверяване | ||||
|   statuses: | ||||
|     open_in_web: Отвори в уеб | ||||
|     over_character_limit: прехвърлен лимит от %{max} символа | ||||
| @@ -155,7 +155,7 @@ bg: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%d %b, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     description_html: При активация на <strong>двустепенно удостоверяване</strong>, за да влезеш в приложението, ще трябва да използваш телефона си. През него ще се генерира код, който да въвеждаш при влизане. | ||||
|     disable: Деактивирай | ||||
|     enable: Активирай | ||||
|   | ||||
| @@ -277,7 +277,7 @@ en: | ||||
|     import: Import | ||||
|     preferences: Preferences | ||||
|     settings: Settings | ||||
|     two_factor_auth: Two-factor Authentication | ||||
|     two_factor_authentication: Two-factor Authentication | ||||
|   statuses: | ||||
|     open_in_web: Open in web | ||||
|     over_character_limit: character limit of %{max} exceeded | ||||
| @@ -293,7 +293,7 @@ en: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: Enter the code generated by your authenticator app to confirm | ||||
|     description_html: If you enable <strong>two-factor authentication</strong>, logging in will require you to be in possession of your phone, which will generate tokens for you to enter. | ||||
|     disable: Disable | ||||
|   | ||||
| @@ -134,7 +134,7 @@ eo: | ||||
|     import: Alporti | ||||
|     preferences: Preferoj | ||||
|     settings: Agordoj | ||||
|     two_factor_auth: Dufaktora aŭtentigo | ||||
|     two_factor_authentication: Dufaktora aŭtentigo | ||||
|   statuses: | ||||
|     open_in_web: Malfermi retumile | ||||
|     over_character_limit: limo de %{max} signoj trapasita | ||||
| @@ -150,7 +150,7 @@ eo: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     description_html: Se vi ebligas <strong>dufaktoran aŭtentigon</strong>, vi bezonos vian poŝtelefonon por ensaluti, ĉar ĝi kreos nombrojn, kiujn vi devos entajpi. | ||||
|     disable: Malebligi | ||||
|     enable: Ebligi | ||||
|   | ||||
| @@ -139,7 +139,7 @@ es: | ||||
|     import: Importar | ||||
|     preferences: Preferencias | ||||
|     settings: Ajustes | ||||
|     two_factor_auth: Autenticación de dos factores | ||||
|     two_factor_authentication: Autenticación de dos factores | ||||
|   statuses: | ||||
|     open_in_web: Abrir en web | ||||
|     over_character_limit: Límite de caracteres de %{max} superado | ||||
| @@ -155,7 +155,7 @@ es: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     description_html: Sí habilitas la <strong>autenticación de dos factores</strong>, se requerirá estar en posesión de su teléfono, lo que generará tokens para que usted pueda iniciar sesión. | ||||
|     disable: Deshabilitar | ||||
|     enable: Habilitar | ||||
|   | ||||
| @@ -134,7 +134,7 @@ fi: | ||||
|     import: Tuo dataa | ||||
|     preferences: Ominaisuudet | ||||
|     settings: Asetukset | ||||
|     two_factor_auth: Kaksivaiheinen tunnistus | ||||
|     two_factor_authentication: Kaksivaiheinen tunnistus | ||||
|   statuses: | ||||
|     open_in_web: Avaa webissä | ||||
|     over_character_limit: sallittu kirjanmäärä %{max} ylitetty | ||||
| @@ -150,7 +150,7 @@ fi: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     description_html: Jos otat käyttöön <strong>kaksivaiheisen tunnistuksen</stron>, kirjautumiseen vaaditaan puhelin, joka voi luoda tokeneita kirjautumista varten. | ||||
|     disable: Poista käytöstä | ||||
|     enable: Ota käyttöön | ||||
|   | ||||
| @@ -273,7 +273,7 @@ fr: | ||||
|     import: Import de données | ||||
|     preferences: Préférences | ||||
|     settings: Réglages | ||||
|     two_factor_auth: Identification à deux facteurs (Two-factor auth) | ||||
|     two_factor_authentication: Identification à deux facteurs (Two-factor auth) | ||||
|   statuses: | ||||
|     open_in_web: Ouvrir sur le web | ||||
|     over_character_limit: limite de caractères dépassée de %{max} caractères | ||||
| @@ -289,7 +289,7 @@ fr: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%d %b %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: Entrez le code généré par votre application pour confirmer | ||||
|     description_html: Si vous activez <strong>l'identification à deux facteurs</strong>, vous devrez être en possession de votre téléphone afin de générer un code de connexion. | ||||
|     disable: Désactiver | ||||
|   | ||||
| @@ -136,7 +136,7 @@ hr: | ||||
|     import: Uvezi | ||||
|     preferences: Postavke | ||||
|     settings: Podešenja | ||||
|     two_factor_auth: Dvo-faktorska Autentifikacija | ||||
|     two_factor_authentication: Dvo-faktorska Autentifikacija | ||||
|   statuses: | ||||
|     open_in_web: Otvori na webu | ||||
|     over_character_limit: prijeđen je limit od %{max} znakova | ||||
| @@ -152,7 +152,7 @@ hr: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     description_html: Ako omogućiš <strong>dvo-faktorsku autentifikaciju</strong>, prijavljivanje će zahtjevati da kod sebe imaš svoj mobitel, koji će generirati tokene koje ćeš unijeti. | ||||
|     disable: Onemogući | ||||
|     enable: Omogući | ||||
|   | ||||
| @@ -250,7 +250,7 @@ id: | ||||
|     import: Impor | ||||
|     preferences: Pilihan | ||||
|     settings: Pengaturan | ||||
|     two_factor_auth: Autentikasi Two-factor | ||||
|     two_factor_authentication: Autentikasi Two-factor | ||||
|   statuses: | ||||
|     open_in_web: Buka di web | ||||
|     over_character_limit: melebihi %{max} karakter | ||||
| @@ -266,7 +266,7 @@ id: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%d %b %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: Masukkan kode yang dibuat oleh app autentikator sebagai konfirmasi | ||||
|     description_html: Jika anda menaktifkan ototentikasi dua faktor, saat login anda harus menggunakan telepon anda  untuk membuat token supaya anda bisa masuk. | ||||
|     disable: Matikan | ||||
|   | ||||
| @@ -276,7 +276,7 @@ io: | ||||
|     import: Importacar | ||||
|     preferences: Preferi | ||||
|     settings: Settings | ||||
|     two_factor_auth: Dufaktora autentikigo | ||||
|     two_factor_authentication: Dufaktora autentikigo | ||||
|   statuses: | ||||
|     open_in_web: Apertar retnavigile | ||||
|     over_character_limit: limito de %{max} signi ecesita | ||||
| @@ -292,7 +292,7 @@ io: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: Enter the code generated by your authenticator app to confirm | ||||
|     description_html: Se tu posibligas <strong>dufaktora autentikigo</strong>, tu bezonos tua poshtelefonilo por enirar, nam ol kreos nombri, quin tu devos enskribar. | ||||
|     disable: Extingar | ||||
|   | ||||
| @@ -140,7 +140,7 @@ it: | ||||
|     import: Importa | ||||
|     preferences: Preferenze | ||||
|     settings: Impostazioni | ||||
|     two_factor_auth: Autenticazione a Due Fattori | ||||
|     two_factor_authentication: Autenticazione a Due Fattori | ||||
|   statuses: | ||||
|     open_in_web: Apri sul Web | ||||
|     over_character_limit: Limite caratteri superato di %{max} | ||||
| @@ -156,7 +156,7 @@ it: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: Inserisci il codice generato dalla tua app di autenticazione | ||||
|     description_html: Se abiliti <strong>l'autorizzazione a due fattori</strong>, entrare nel tuo account ti richiederà di avere vicino il tuo telefono, il quale ti genererà un codice per eseguire l'accesso. | ||||
|     disable: Disabilita | ||||
|   | ||||
| @@ -276,7 +276,7 @@ ja: | ||||
|     import: データのインポート | ||||
|     preferences: ユーザー設定 | ||||
|     settings: 設定 | ||||
|     two_factor_auth: 二段階認証 | ||||
|     two_factor_authentication: 二段階認証 | ||||
|   statuses: | ||||
|     open_in_web: Webで開く | ||||
|     over_character_limit: 上限は %{max}文字までです | ||||
| @@ -292,7 +292,7 @@ ja: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%Y年%m月%d日 %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: 確認するには認証アプリで表示されたコードを入力してください | ||||
|     description_html: "<strong>二段階認証</strong>を有効にするとログイン時、電話でコードを受け取る必要があります。" | ||||
|     disable: 無効 | ||||
|   | ||||
| @@ -137,7 +137,7 @@ nl: | ||||
|     import: Import | ||||
|     preferences: Voorkeuren | ||||
|     settings: Instellingen | ||||
|     two_factor_auth: Tweestapsverificatie | ||||
|     two_factor_authentication: Tweestapsverificatie | ||||
|   statuses: | ||||
|     open_in_web: Openen in web | ||||
|     over_character_limit: Limiet van %{max} tekens overschreden | ||||
| @@ -153,7 +153,7 @@ nl: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%d %B %J om %U:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     description_html: Na het instellen van <strong>tweestapsverificatie</strong>, kun jij je alleen aanmelden als je jouw mobiele telefoon bij je hebt. Hiermee genereer je namelijk de in te voeren aanmeldcode. | ||||
|     disable: Uitschakelen | ||||
|     enable: Inschakelen | ||||
|   | ||||
| @@ -134,7 +134,7 @@ | ||||
|     import: Importér | ||||
|     preferences: Preferanser | ||||
|     settings: Innstillinger | ||||
|     two_factor_auth: Tofaktorautentisering | ||||
|     two_factor_authentication: Tofaktorautentisering | ||||
|   statuses: | ||||
|     open_in_web: Åpne i nettleser | ||||
|     over_character_limit: grense på %{max} tegn overskredet | ||||
| @@ -150,7 +150,7 @@ | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%d, %b %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     description_html: Hvis du skrur på <strong>tofaktorautentisering</strong> må du ha din telefon for å logge inn. Denne vil generere koder som du må taste inn. | ||||
|     disable: Skru av | ||||
|     enable: Skru på | ||||
|   | ||||
| @@ -292,7 +292,7 @@ oc: | ||||
|     import: Import | ||||
|     preferences: Preferéncias | ||||
|     settings: Paramètres | ||||
|     two_factor_auth: Autentificacion en dos temps | ||||
|     two_factor_authentication: Autentificacion en dos temps | ||||
|   statuses: | ||||
|     open_in_web: Dobrir sul web | ||||
|     over_character_limit: limit de %{max} caractèrs passat | ||||
| @@ -308,7 +308,7 @@ oc: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d %Y a %H o %M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: Enter the code generated by your authenticator app to confirm | ||||
|     description_html: S’activatz <strong> l’autentificacion two-factor</strong>, vos | ||||
|       caldrà vòstre mobil per vos connectar perque generarà un geton per vos daissar | ||||
|   | ||||
| @@ -134,7 +134,7 @@ pl: | ||||
|     import: Importuj dane | ||||
|     preferences: Preferencje | ||||
|     settings: Ustawienia | ||||
|     two_factor_auth: Uwierzytelnianie dwuetapowe | ||||
|     two_factor_authentication: Uwierzytelnianie dwuetapowe | ||||
|   statuses: | ||||
|     open_in_web: Otwórz w przeglądarce | ||||
|     over_character_limit: limit %{max} znaków przekroczony | ||||
| @@ -150,7 +150,7 @@ pl: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     description_html: Jeśli włączysz <strong>uwierzytelnianie dwustopniowe</strong>, logowanie się będzie wymagało podania tokenu wyświetlonego na Twoim telefone. | ||||
|     disable: Wyłącz | ||||
|     enable: Włącz | ||||
|   | ||||
| @@ -267,7 +267,7 @@ ru: | ||||
|     import: Импорт | ||||
|     preferences: Настройки | ||||
|     settings: Опции | ||||
|     two_factor_auth: Двухфакторная аутентификация | ||||
|     two_factor_authentication: Двухфакторная аутентификация | ||||
|   statuses: | ||||
|     open_in_web: Открыть в WWW | ||||
|     over_character_limit: превышен лимит символов (%{max}) | ||||
| @@ -283,7 +283,7 @@ ru: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%b %d, %Y, %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: Для подтверждения введите код, сгенерированный приложением аутентификатора | ||||
|     description_html: При включении <strong>двухфакторной аутентификации</strong>, вход потребует от Вас использования Вашего телефона, который сгенерирует входные токены. | ||||
|     disable: Отключить | ||||
|   | ||||
| @@ -269,7 +269,7 @@ zh-CN: | ||||
|     export: 数据导出 | ||||
|     preferences: 首选项 | ||||
|     settings: 设置 | ||||
|     two_factor_auth: 两步认证 | ||||
|     two_factor_authentication: 两步认证 | ||||
|   statuses: | ||||
|     # Hey, this is already in a web browser! | ||||
|     open_in_web: 打开网页 | ||||
| @@ -286,7 +286,7 @@ zh-CN: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%Y年%-m月%d日 %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: 请输入你认证器产生的代码,以确认设置 | ||||
|     description_html: 当你启用<strong>两步认证</strong>后,你登录时将额外需要使用手机或其他认证器生成的代码。 | ||||
|     disable: 停用 | ||||
|   | ||||
| @@ -276,7 +276,7 @@ zh-HK: | ||||
|     import: 匯入 | ||||
|     preferences: 偏好設定 | ||||
|     settings: 設定 | ||||
|     two_factor_auth: 雙重認證 | ||||
|     two_factor_authentication: 雙重認證 | ||||
|   statuses: | ||||
|     open_in_web: 開啟網頁 | ||||
|     over_character_limit: 超過了 %{max} 字的限制 | ||||
| @@ -292,7 +292,7 @@ zh-HK: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%Y年%-m月%d日 %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: 請輸入你認證器產生的代碼,以確認設定 | ||||
|     description_html: 當你啟用<strong>雙重認證</strong>後,你登入時將需要使你手機、或其他種類認證器產生的代碼。 | ||||
|     disable: 停用 | ||||
|   | ||||
| @@ -248,7 +248,7 @@ zh-TW: | ||||
|     import: 匯入 | ||||
|     preferences: 偏好設定 | ||||
|     settings: 設定 | ||||
|     two_factor_auth: 雙因子認證 | ||||
|     two_factor_authentication: 雙因子認證 | ||||
|   statuses: | ||||
|     open_in_web: 以網頁開啟 | ||||
|     over_character_limit: 超過了 %{max} 字的限制 | ||||
| @@ -264,7 +264,7 @@ zh-TW: | ||||
|   time: | ||||
|     formats: | ||||
|       default: "%Y年%-m月%d日 %H:%M" | ||||
|   two_factor_auth: | ||||
|   two_factor_authentication: | ||||
|     code_hint: 請輸入您認證器產生的代碼,以進行認證 | ||||
|     description_html: 當您啟用<strong>雙因子認證</strong>後,您登入時將需要使您手機、或其他種類認證器產生的代碼。 | ||||
|     disable: 停用 | ||||
|   | ||||
| @@ -8,7 +8,7 @@ SimpleNavigation::Configuration.run do |navigation| | ||||
|       settings.item :profile, safe_join([fa_icon('user fw'), t('settings.edit_profile')]), settings_profile_url | ||||
|       settings.item :preferences, safe_join([fa_icon('sliders fw'), t('settings.preferences')]), settings_preferences_url | ||||
|       settings.item :password, safe_join([fa_icon('cog fw'), t('auth.change_password')]), edit_user_registration_url | ||||
|       settings.item :two_factor_auth, safe_join([fa_icon('mobile fw'), t('settings.two_factor_auth')]), settings_two_factor_auth_url, highlights_on: %r{/settings/two_factor_auth} | ||||
|       settings.item :two_factor_authentication, safe_join([fa_icon('mobile fw'), t('settings.two_factor_authentication')]), settings_two_factor_authentication_url, highlights_on: %r{/settings/two_factor_authentication} | ||||
|       settings.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url | ||||
|       settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url | ||||
|       settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url | ||||
|   | ||||
| @@ -58,11 +58,10 @@ Rails.application.routes.draw do | ||||
|       resources :mutes, only: :index, controller: :muted_accounts | ||||
|     end | ||||
|  | ||||
|     resource :two_factor_auth, only: [:show, :new, :create] do | ||||
|       member do | ||||
|         post :disable | ||||
|         post :recovery_codes | ||||
|       end | ||||
|     resource :two_factor_authentication, only: [:show, :create, :destroy] | ||||
|     namespace :two_factor_authentication do | ||||
|       resources :recovery_codes, only: [:create] | ||||
|       resource :confirmation, only: [:new, :create] | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,46 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| require 'rails_helper' | ||||
|  | ||||
| describe Settings::TwoFactorAuthentication::ConfirmationsController do | ||||
|   render_views | ||||
|  | ||||
|   let(:user) { Fabricate(:user) } | ||||
|   before do | ||||
|     user.otp_secret = User.generate_otp_secret(32) | ||||
|     user.save! | ||||
|  | ||||
|     sign_in user, scope: :user | ||||
|   end | ||||
|  | ||||
|   describe 'GET #new' do | ||||
|     it 'returns http success' do | ||||
|       get :new | ||||
|  | ||||
|       expect(response).to have_http_status(:success) | ||||
|       expect(response).to render_template(:new) | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   describe 'POST #create' do | ||||
|     describe 'when creation succeeds' do | ||||
|       it 'renders page with success' do | ||||
|         allow_any_instance_of(User).to receive(:validate_and_consume_otp!).with('123456').and_return(true) | ||||
|  | ||||
|         post :create, params: { form_two_factor_confirmation: { code: '123456' } } | ||||
|         expect(response).to have_http_status(:success) | ||||
|         expect(response).to render_template('settings/two_factor_authentication/recovery_codes/index') | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     describe 'when creation fails' do | ||||
|       it 'renders the new view' do | ||||
|         allow_any_instance_of(User).to receive(:validate_and_consume_otp!).with('123456').and_return(false) | ||||
|  | ||||
|         post :create, params: { form_two_factor_confirmation: { code: '123456' } } | ||||
|         expect(response).to have_http_status(:success) | ||||
|         expect(response).to render_template(:new) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,25 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| require 'rails_helper' | ||||
|  | ||||
| describe Settings::TwoFactorAuthentication::RecoveryCodesController do | ||||
|   render_views | ||||
|  | ||||
|   let(:user) { Fabricate(:user) } | ||||
|   before do | ||||
|     sign_in user, scope: :user | ||||
|   end | ||||
|  | ||||
|   describe 'POST #create' do | ||||
|     it 'updates the codes and shows them on a view' do | ||||
|       before = user.otp_backup_codes | ||||
|  | ||||
|       post :create | ||||
|       user.reload | ||||
|  | ||||
|       expect(user.otp_backup_codes).not_to eq(before) | ||||
|       expect(response).to have_http_status(:success) | ||||
|       expect(response).to render_template(:index) | ||||
|     end | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,66 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| require 'rails_helper' | ||||
|  | ||||
| describe Settings::TwoFactorAuthenticationsController do | ||||
|   render_views | ||||
|  | ||||
|   let(:user) { Fabricate(:user) } | ||||
|   before do | ||||
|     sign_in user, scope: :user | ||||
|   end | ||||
|  | ||||
|   describe 'GET #show' do | ||||
|     describe 'when user requires otp for login already' do | ||||
|       it 'returns http success' do | ||||
|         user.update(otp_required_for_login: true) | ||||
|         get :show | ||||
|  | ||||
|         expect(response).to have_http_status(:success) | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     describe 'when user does not require otp for login' do | ||||
|       it 'returns http success' do | ||||
|         user.update(otp_required_for_login: false) | ||||
|         get :show | ||||
|  | ||||
|         expect(response).to have_http_status(:success) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   describe 'POST #create' do | ||||
|     describe 'when user requires otp for login already' do | ||||
|       it 'redirects to show page' do | ||||
|         user.update(otp_required_for_login: true) | ||||
|         post :create | ||||
|  | ||||
|         expect(response).to redirect_to(settings_two_factor_authentication_path) | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     describe 'when creation succeeds' do | ||||
|       it 'updates user secret' do | ||||
|         before = user.otp_secret | ||||
|         post :create | ||||
|  | ||||
|         expect(user.reload.otp_secret).not_to eq(before) | ||||
|         expect(response).to redirect_to(new_settings_two_factor_authentication_confirmation_path) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   describe 'POST #destroy' do | ||||
|     before do | ||||
|       user.update(otp_required_for_login: true) | ||||
|     end | ||||
|     it 'turns off otp requirement' do | ||||
|       post :destroy | ||||
|  | ||||
|       expect(response).to redirect_to(settings_two_factor_authentication_path) | ||||
|       user.reload | ||||
|       expect(user.otp_required_for_login).to eq(false) | ||||
|     end | ||||
|   end | ||||
| end | ||||
		Reference in New Issue
	
	Block a user