mirror of
https://github.com/mastodon/mastodon.git
synced 2025-09-04 00:43:41 +00:00
Compare commits
35 Commits
9df69fb089
...
27efe7dbfd
Author | SHA1 | Date | |
---|---|---|---|
![]() |
27efe7dbfd | ||
![]() |
733587c4bf | ||
![]() |
e259a67151 | ||
![]() |
a5b2b564ab | ||
![]() |
6d82a3d2a5 | ||
![]() |
879fd3406e | ||
![]() |
50298a1ce1 | ||
![]() |
d114645e60 | ||
![]() |
8bd9d60cdf | ||
![]() |
cf1db1a025 | ||
![]() |
4ce99fcf55 | ||
![]() |
1f6f3bf01a | ||
![]() |
7af438159a | ||
![]() |
7e1eeff268 | ||
![]() |
2b98d29942 | ||
![]() |
6a40447759 | ||
![]() |
63d0efa0c2 | ||
![]() |
bdf102df4e | ||
![]() |
e5f4362b8f | ||
![]() |
26081d66fd | ||
![]() |
a8f5e3fa62 | ||
![]() |
f5c14b753a | ||
![]() |
1073956fbc | ||
![]() |
673d875a95 | ||
![]() |
7a957b1f49 | ||
![]() |
6fda7a9f56 | ||
![]() |
1c52aa76eb | ||
![]() |
df97c75d34 | ||
![]() |
659c695a5a | ||
![]() |
86e38dc4f6 | ||
![]() |
7b1f26eea2 | ||
![]() |
6cc6c36c44 | ||
![]() |
16b19f48cc | ||
![]() |
4e1d61ecd0 | ||
![]() |
218a9e70c5 |
|
@ -109,3 +109,6 @@ FETCH_REPLIES_MAX_SINGLE=500
|
|||
|
||||
# Max number of replies Collection pages to fetch - total
|
||||
FETCH_REPLIES_MAX_PAGES=500
|
||||
|
||||
# MFA Required for Users
|
||||
REQUIRE_MULTI_FACTOR_AUTH=false
|
|
@ -14,6 +14,7 @@ class ApplicationController < ActionController::Base
|
|||
include DatabaseHelper
|
||||
include AuthorizedFetchHelper
|
||||
include SelfDestructHelper
|
||||
include MfaForceConcern
|
||||
|
||||
helper_method :current_account
|
||||
helper_method :current_session
|
||||
|
|
|
@ -18,6 +18,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
|
||||
skip_before_action :check_self_destruct!, only: [:edit, :update]
|
||||
skip_before_action :require_functional!, only: [:edit, :update]
|
||||
skip_before_action :check_mfa_requirement, only: [:edit, :update]
|
||||
|
||||
def new
|
||||
super(&:build_invite_request)
|
||||
|
@ -100,12 +101,13 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_invite
|
||||
@invite = begin
|
||||
invite = Invite.find_by(code: invite_code) if invite_code.present?
|
||||
invite if invite&.valid_for_use?
|
||||
if invite_code.present?
|
||||
Invite.find_by(code: invite_code)
|
||||
elsif params[:invite_code].present?
|
||||
Invite.find_by(code: params[:invite_code])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -132,15 +134,13 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
def require_rules_acceptance!
|
||||
return if @rules.empty? || (session[:accept_token].present? && params[:accept] == session[:accept_token])
|
||||
|
||||
@accept_token = session[:accept_token] = SecureRandom.hex
|
||||
@invite_code = invite_code
|
||||
|
||||
set_locale { render :rules }
|
||||
session[:accept_token] = SecureRandom.hex(16)
|
||||
redirect_to new_user_registration_path(accept: session[:accept_token])
|
||||
end
|
||||
|
||||
def is_flashing_format? # rubocop:disable Naming/PredicatePrefix
|
||||
if params[:action] == 'create'
|
||||
false # Disable flash messages for sign-up
|
||||
false
|
||||
else
|
||||
super
|
||||
end
|
||||
|
|
|
@ -11,6 +11,7 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
skip_before_action :require_no_authentication, only: [:create]
|
||||
skip_before_action :require_functional!
|
||||
skip_before_action :update_user_sign_in
|
||||
skip_before_action :check_mfa_requirement, only: [:destroy]
|
||||
|
||||
around_action :preserve_stored_location, only: :destroy, if: :continue_after?
|
||||
|
||||
|
@ -199,12 +200,8 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
|
||||
def respond_to_on_destroy
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
redirect_to: after_sign_out_path_for(resource_name),
|
||||
}, status: 200
|
||||
end
|
||||
format.all { super }
|
||||
format.any(*navigational_formats) { redirect_to after_sign_out_path_for(:user) }
|
||||
format.all { head 204 }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ class Auth::SetupController < ApplicationController
|
|||
before_action :set_user
|
||||
|
||||
skip_before_action :require_functional!
|
||||
skip_before_action :check_mfa_requirement
|
||||
|
||||
def show; end
|
||||
|
||||
|
|
27
app/controllers/concerns/mfa_force_concern.rb
Normal file
27
app/controllers/concerns/mfa_force_concern.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module MfaForceConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
prepend_before_action :check_mfa_requirement, if: :user_signed_in?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_mfa_requirement
|
||||
return unless mfa_force_enabled?
|
||||
return if current_user.otp_enabled?
|
||||
|
||||
flash[:alert] = I18n.t('require_multi_factor_auth.required_message')
|
||||
redirect_to settings_otp_authentication_path
|
||||
end
|
||||
|
||||
def mfa_force_enabled?
|
||||
mfa_config[:force_enabled]
|
||||
end
|
||||
|
||||
def mfa_config
|
||||
@mfa_config ||= Rails.application.config_for(:mfa)
|
||||
end
|
||||
end
|
|
@ -6,6 +6,7 @@ module Settings
|
|||
include ChallengableConcern
|
||||
|
||||
skip_before_action :require_functional!
|
||||
skip_before_action :check_mfa_requirement
|
||||
|
||||
before_action :require_challenge!
|
||||
before_action :ensure_otp_secret
|
||||
|
|
|
@ -6,6 +6,7 @@ module Settings
|
|||
include ChallengableConcern
|
||||
|
||||
skip_before_action :require_functional!
|
||||
skip_before_action :check_mfa_requirement
|
||||
|
||||
before_action :verify_otp_not_enabled, only: [:show]
|
||||
before_action :require_challenge!, only: [:create]
|
||||
|
|
|
@ -6,6 +6,7 @@ module Settings
|
|||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
skip_before_action :check_mfa_requirement
|
||||
|
||||
before_action :require_challenge!, only: :disable
|
||||
before_action :require_otp_enabled
|
||||
|
|
|
@ -2148,3 +2148,7 @@ en:
|
|||
not_supported: This browser doesn't support security keys
|
||||
otp_required: To use security keys please enable two-factor authentication first.
|
||||
registered_on: Registered on %{date}
|
||||
|
||||
require_multi_factor_auth:
|
||||
required_message: The administrator of this site has configured as mandatory that users enable two-factor authentication due to security policies. Please configure your two-factor authentication to continue using the platform.
|
||||
security_policy: Security Policy Requirement
|
||||
|
|
|
@ -2146,4 +2146,4 @@ es:
|
|||
not_enabled: Aún no has activado WebAuthn
|
||||
not_supported: Este navegador no soporta claves de seguridad
|
||||
otp_required: Para usar claves de seguridad, por favor habilite primero la autenticación de doble factor.
|
||||
registered_on: Registrado el %{date}
|
||||
registered_on: Registrado el %{date}
|
15
config/mfa.yml
Normal file
15
config/mfa.yml
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Multi-Factor Authentication configuration
|
||||
default: &default
|
||||
force_enabled: false
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
force_enabled: <%= ENV.fetch('REQUIRE_MULTI_FACTOR_AUTH', 'false') == 'true' %>
|
||||
|
||||
test:
|
||||
<<: *default
|
||||
force_enabled: false
|
||||
|
||||
production:
|
||||
<<: *default
|
||||
force_enabled: <%= ENV.fetch('REQUIRE_MULTI_FACTOR_AUTH', 'false') == 'true' %>
|
111
spec/controllers/concerns/mfa_force_concern_spec.rb
Normal file
111
spec/controllers/concerns/mfa_force_concern_spec.rb
Normal file
|
@ -0,0 +1,111 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe MfaForceConcern do
|
||||
controller(ApplicationController) do
|
||||
def index
|
||||
render plain: 'OK'
|
||||
end
|
||||
end
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
routes.draw { get 'index' => 'anonymous#index' }
|
||||
end
|
||||
|
||||
describe 'MFA force functionality' do
|
||||
context 'when REQUIRE_MULTI_FACTOR_AUTH is enabled' do
|
||||
before do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'true') do
|
||||
sign_in user, scope: :user
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has MFA enabled' do
|
||||
before do
|
||||
user.update(otp_required_for_login: true)
|
||||
end
|
||||
|
||||
it 'allows access to normal pages' do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'true') do
|
||||
get :index
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does not have MFA enabled' do
|
||||
before do
|
||||
user.update(otp_required_for_login: false)
|
||||
end
|
||||
|
||||
it 'redirects to MFA setup page' do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'true') do
|
||||
get :index
|
||||
expect(response).to redirect_to(settings_otp_authentication_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows the required message' do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'true') do
|
||||
get :index
|
||||
expect(flash[:alert]).to eq(I18n.t('require_multi_factor_auth.required_message'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when accessing MFA setup pages' do
|
||||
it 'allows access to OTP authentication page' do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'true') do
|
||||
allow(controller.request).to receive(:path).and_return('/settings/otp_authentication')
|
||||
get :index
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows access to MFA confirmation page' do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'true') do
|
||||
allow(controller.request).to receive(:path).and_return('/settings/two_factor_authentication/confirmation')
|
||||
get :index
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows access to logout' do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'true') do
|
||||
allow(controller.request).to receive(:path).and_return('/auth/sign_out')
|
||||
get :index
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when REQUIRE_MULTI_FACTOR_AUTH is disabled' do
|
||||
before do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'false') do
|
||||
sign_in user, scope: :user
|
||||
user.update(otp_required_for_login: false)
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows access to normal pages' do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'false') do
|
||||
get :index
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not signed in' do
|
||||
it 'allows access to normal pages' do
|
||||
ClimateControl.modify(REQUIRE_MULTI_FACTOR_AUTH: 'true') do
|
||||
get :index
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user