Add ability to require 2FA for specific roles (including Everybody) (#37701)

This commit is contained in:
Claire authored and GitHub committed 2026-02-11 14:34:09 +00:00
1 parent 3e1127d27b
commit dfe44bcaef
23 files changed
+184 -15

No files matched your search

+1 -1
View File
@@ -62,7 +62,7 @@ module Admin
def resource_params
params
.expect(user_role: [:name, :color, :highlighted, :position, permissions_as_keys: []])
.expect(user_role: [:name, :color, :highlighted, :position, :require_2fa, permissions_as_keys: []])
end
end
end
+10 -2
View File
@@ -61,19 +61,25 @@ class ApplicationController < ActionController::Base
return if request.referer.blank?
redirect_uri = URI(request.referer)
return if redirect_uri.path.start_with?('/auth')
return if redirect_uri.path.start_with?('/auth', '/settings/two_factor_authentication', '/settings/otp_authentication')
stored_url = redirect_uri.to_s if redirect_uri.host == request.host && redirect_uri.port == request.port
store_location_for(:user, stored_url)
end
def mfa_setup_path(path_params = {})
settings_two_factor_authentication_methods_path(path_params)
end
def require_functional!
return if current_user.functional?
respond_to do |format|
format.any do
if current_user.confirmed?
if current_user.missing_2fa?
redirect_to mfa_setup_path
elsif current_user.confirmed?
redirect_to edit_user_registration_path
else
redirect_to auth_setup_path
@@ -85,6 +91,8 @@ class ApplicationController < ActionController::Base
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
elsif !current_user.approved?
render json: { error: 'Your login is currently pending approval' }, status: 403
elsif current_user.missing_2fa?
render json: { error: 'Your account requires two-factor authentication' }, status: 403
elsif !current_user.functional?
render json: { error: 'Your login is currently disabled' }, status: 403
end
@@ -42,7 +42,7 @@ module ChallengableConcern
end
def render_challenge
render 'auth/challenges/new', layout: 'auth'
render 'auth/challenges/new', layout: params[:oauth] ? 'modal' : 'auth'
end
def challenge_passed?
@@ -24,4 +24,8 @@ class OAuth::AuthorizationsController < Doorkeeper::AuthorizationsController
def truthy_param?(key)
ActiveModel::Type::Boolean.new.cast(params[key])
end
def mfa_setup_path
super({ oauth: true })
end
end
@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Settings
module TwoFactorAuthentication
class BaseController < ::Settings::BaseController
layout -> { truthy_param?(:oauth) ? 'modal' : 'admin' }
end
end
end
@@ -4,12 +4,15 @@ module Settings
module TwoFactorAuthentication
class ConfirmationsController < BaseController
include ChallengableConcern
include Devise::Controllers::StoreLocation
skip_before_action :require_functional!
before_action :require_challenge!
before_action :ensure_otp_secret
helper_method :return_to_app_url
def new
prepare_two_factor_form
end
@@ -37,6 +40,10 @@ module Settings
private
def return_to_app_url
stored_location_for(:user)
end
def confirmation_params
params.expect(form_two_factor_confirmation: [:otp_attempt])
end
@@ -17,7 +17,7 @@ module Settings
def create
session[:new_otp_secret] = User.generate_otp_secret
redirect_to new_settings_two_factor_authentication_confirmation_path
redirect_to new_settings_two_factor_authentication_confirmation_path(params.permit(:oauth))
end
private
@@ -22,7 +22,7 @@ module Settings
private
def require_otp_enabled
redirect_to settings_otp_authentication_path unless current_user.otp_enabled?
redirect_to settings_otp_authentication_path(params.permit(:oauth)) unless current_user.otp_enabled?
end
end
end