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 neededpull/15/head
parent
6af21daac9
commit
67dea31b0f
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue