diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e15d83631b3..978a269ca52 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -225,9 +225,13 @@ class ApplicationController < ActionController::Base end def check_tfa_requirement - if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled + if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled && !skip_two_factor? + grace_period_started = current_user.otp_grace_period_started_at + grace_period_deadline = grace_period_started + two_factor_grace_period.hours + + deadline_text = "until #{l(grace_period_deadline)}" unless two_factor_grace_period_expired?(grace_period_started) redirect_to new_profile_two_factor_auth_path, - alert: 'You must configure Two-Factor Authentication in your account' + alert: "You must configure Two-Factor Authentication in your account #{deadline_text}" end end @@ -369,6 +373,18 @@ class ApplicationController < ActionController::Base current_application_settings.require_two_factor_authentication end + def two_factor_grace_period + current_application_settings.two_factor_grace_period + end + + def two_factor_grace_period_expired?(date) + date && (date + two_factor_grace_period.hours) < Time.current + end + + def skip_two_factor? + session[:skip_tfa] && session[:skip_tfa] > Time.current + end + def redirect_to_home_page_url? # If user is not signed-in and tries to access root_path - redirect him to landing page # Don't redirect to the default URL to prevent endless redirections diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index 05c84fb720e..49629e9894a 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -4,8 +4,11 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController def new unless current_user.otp_secret current_user.otp_secret = User.generate_otp_secret(32) - current_user.save! end + unless current_user.otp_grace_period_started_at && two_factor_grace_period + current_user.otp_grace_period_started_at = Time.current + end + current_user.save! if current_user.changed? @qr_code = build_qr_code end @@ -36,6 +39,15 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController redirect_to profile_account_path end + def skip + if two_factor_grace_period_expired?(current_user.otp_grace_period_started_at) + redirect_to new_profile_two_factor_auth_path, alert: 'Cannot skip two factor authentication setup' + else + session[:skip_tfa] = current_user.otp_grace_period_started_at + two_factor_grace_period.hours + redirect_to root_path + end + end + private def build_qr_code diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index 2c81ea1623c..0cfc0565e84 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -50,5 +50,17 @@ module AuthHelper current_user.identities.exists?(provider: provider.to_s) end + def two_factor_skippable? + current_application_settings.require_two_factor_authentication && + !current_user.two_factor_enabled && + current_application_settings.two_factor_grace_period && + !two_factor_grace_period_expired? + end + + def two_factor_grace_period_expired? + current_user.otp_grace_period_started_at && + (current_user.otp_grace_period_started_at + current_application_settings.two_factor_grace_period.hours) < Time.current + end + extend self end diff --git a/app/views/profiles/two_factor_auths/new.html.haml b/app/views/profiles/two_factor_auths/new.html.haml index 92dc58c10d7..1a5b6efce35 100644 --- a/app/views/profiles/two_factor_auths/new.html.haml +++ b/app/views/profiles/two_factor_auths/new.html.haml @@ -38,3 +38,4 @@ = text_field_tag :pin_code, nil, class: "form-control", required: true, autofocus: true .form-actions = submit_tag 'Submit', class: 'btn btn-success' + = link_to 'Configure it later', skip_profile_two_factor_auth_path, :method => :patch, class: 'btn btn-cancel' if two_factor_skippable? diff --git a/config/routes.rb b/config/routes.rb index b9242327de1..3e7d9f78710 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -297,6 +297,7 @@ Rails.application.routes.draw do resource :two_factor_auth, only: [:new, :create, :destroy] do member do post :codes + patch :skip end end end diff --git a/db/migrate/20151221234414_add_tfa_additional_fields.rb b/db/migrate/20151221234414_add_tfa_additional_fields.rb new file mode 100644 index 00000000000..c16df47932f --- /dev/null +++ b/db/migrate/20151221234414_add_tfa_additional_fields.rb @@ -0,0 +1,7 @@ +class AddTfaAdditionalFields < ActiveRecord::Migration + def change + change_table :users do |t| + t.datetime :otp_grace_period_started_at, null: true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 631979a7fd8..49fa258660d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -840,6 +840,7 @@ ActiveRecord::Schema.define(version: 20151224123230) do t.integer "layout", default: 0 t.boolean "hide_project_limit", default: false t.string "unlock_token" + t.datetime "otp_grace_period_started_at" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree