diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index aa08ae4d..312f71cd 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -5,6 +5,7 @@ class DashboardController < ApplicationController before_action :maybe_redirect_product_url before_action :maybe_render_landing + before_action :maybe_redirect_mfa_setup load_and_authorize_resource :template_folder, parent: false load_and_authorize_resource :template, parent: false @@ -62,6 +63,17 @@ class DashboardController < ApplicationController redirect_to Docuseal::PRODUCT_URL, allow_other_host: true end + def maybe_redirect_mfa_setup + return unless signed_in? + return if current_user.otp_required_for_login + + return if !current_user.otp_required_for_login && !AccountConfig.exists?(value: true, + account_id: current_user.account_id, + key: AccountConfig::FORCE_MFA) + + redirect_to mfa_setup_path, notice: 'Setup 2FA to continue' + end + def maybe_render_landing return if signed_in? diff --git a/app/controllers/mfa_force_controller.rb b/app/controllers/mfa_force_controller.rb new file mode 100644 index 00000000..1be78147 --- /dev/null +++ b/app/controllers/mfa_force_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class MfaForceController < ApplicationController + before_action :load_account_config + authorize_resource :account_config + + def create + @account_config.update!(value: !@account_config.value) + + redirect_back fallback_location: settings_users_path, + notice: "Force 2FA has been #{@account_config.value ? 'enabled' : 'disabled'}." + end + + private + + def load_account_config + @account_config = + AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::FORCE_MFA) + end +end diff --git a/app/controllers/mfa_setup_controller.rb b/app/controllers/mfa_setup_controller.rb index ce690643..c65b2006 100644 --- a/app/controllers/mfa_setup_controller.rb +++ b/app/controllers/mfa_setup_controller.rb @@ -5,13 +5,11 @@ class MfaSetupController < ApplicationController authorize!(:update, current_user) end - def new - current_user.otp_secret ||= User.generate_otp_secret + before_action :set_provision_url, only: %i[show new] - current_user.save! + def show; end - @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Docuseal.product_name) - end + def new; end def edit; end @@ -26,7 +24,7 @@ class MfaSetupController < ApplicationController @error_message = 'Code is invalid' - render turbo_stream: turbo_stream.replace(:modal, template: 'mfa_setup/new'), status: :unprocessable_entity + render turbo_stream: turbo_stream.replace(:mfa_form, partial: 'mfa_setup/form'), status: :unprocessable_entity end end @@ -41,4 +39,16 @@ class MfaSetupController < ApplicationController render turbo_stream: turbo_stream.replace(:modal, template: 'mfa_setup/edit'), status: :unprocessable_entity end end + + private + + def set_provision_url + return redirect_to root_path, alert: '2FA has been set up already' if current_user.otp_required_for_login + + current_user.otp_secret ||= User.generate_otp_secret + + current_user.save! + + @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Docuseal.product_name) + end end diff --git a/app/models/account_config.rb b/app/models/account_config.rb index b592cbca..56410a89 100644 --- a/app/models/account_config.rb +++ b/app/models/account_config.rb @@ -25,6 +25,7 @@ class AccountConfig < ApplicationRecord SUBMITTER_COMPLETED_EMAIL_KEY = 'submitter_completed_email' SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY = 'submitter_documents_copy_email' BCC_EMAILS = 'bcc_emails' + FORCE_MFA = 'force_mfa' SUBMITTER_REMAILERS = 'submitter_reminders' FORM_COMPLETED_BUTTON_KEY = 'form_completed_button' diff --git a/app/views/mfa_setup/_form.html.erb b/app/views/mfa_setup/_form.html.erb new file mode 100644 index 00000000..c5060c22 --- /dev/null +++ b/app/views/mfa_setup/_form.html.erb @@ -0,0 +1,17 @@ +<%= form_for '', url: mfa_setup_path, data: { turbo_frame: :_top }, html: { id: 'mfa_form'} do |f| %> +
+ Use an authenticator mobile app like Google Authenticator or 1Password to scan the QR code below. +
+- Use an authenticator mobile app like Google Authenticator or 1Password to scan the QR code below. -
-