mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 22:43:33 +08:00
FIX: require: false for rotp gem (#8540)
The ROTP gem is only used in a very small amount of places in the app, we don't need to globally require it. Also set the Addressable gem to not have a specific version range, as it has not been a problem yet. Some slight refactoring of UserSecondFactor here too to use SecondFactorManager to avoid code repetition
This commit is contained in:
4
Gemfile
4
Gemfile
@ -134,7 +134,7 @@ gem 'highline', '~> 1.7.0', require: false
|
|||||||
gem 'rack-protection' # security
|
gem 'rack-protection' # security
|
||||||
gem 'cbor', require: false
|
gem 'cbor', require: false
|
||||||
gem 'cose', require: false
|
gem 'cose', require: false
|
||||||
gem 'addressable', '~> 2.7.0'
|
gem 'addressable'
|
||||||
|
|
||||||
# Gems used only for assets and not required in production environments by default.
|
# Gems used only for assets and not required in production environments by default.
|
||||||
# Allow everywhere for now cause we are allowing asset debugging in production
|
# Allow everywhere for now cause we are allowing asset debugging in production
|
||||||
@ -229,7 +229,7 @@ gem 'logster'
|
|||||||
gem 'sassc', '2.0.1', require: false
|
gem 'sassc', '2.0.1', require: false
|
||||||
gem "sassc-rails"
|
gem "sassc-rails"
|
||||||
|
|
||||||
gem 'rotp'
|
gem 'rotp', require: false
|
||||||
gem 'rqrcode'
|
gem 'rqrcode'
|
||||||
|
|
||||||
gem 'rubyzip', require: false
|
gem 'rubyzip', require: false
|
||||||
|
@ -437,7 +437,7 @@ DEPENDENCIES
|
|||||||
activemodel (= 6.0.1)
|
activemodel (= 6.0.1)
|
||||||
activerecord (= 6.0.1)
|
activerecord (= 6.0.1)
|
||||||
activesupport (= 6.0.1)
|
activesupport (= 6.0.1)
|
||||||
addressable (~> 2.7.0)
|
addressable
|
||||||
annotate
|
annotate
|
||||||
aws-sdk-s3
|
aws-sdk-s3
|
||||||
aws-sdk-sns
|
aws-sdk-sns
|
||||||
|
@ -1218,6 +1218,7 @@ class UsersController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create_second_factor_totp
|
def create_second_factor_totp
|
||||||
|
require 'rotp' if !defined? ROTP
|
||||||
totp_data = ROTP::Base32.random
|
totp_data = ROTP::Base32.random
|
||||||
secure_session["staged-totp-#{current_user.id}"] = totp_data
|
secure_session["staged-totp-#{current_user.id}"] = totp_data
|
||||||
qrcode_svg = RQRCode::QRCode.new(current_user.totp_provisioning_uri(totp_data)).as_svg(
|
qrcode_svg = RQRCode::QRCode.new(current_user.totp_provisioning_uri(totp_data)).as_svg(
|
||||||
|
@ -6,6 +6,7 @@ module SecondFactorManager
|
|||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def create_totp(opts = {})
|
def create_totp(opts = {})
|
||||||
|
require_rotp
|
||||||
UserSecondFactor.create!({
|
UserSecondFactor.create!({
|
||||||
user_id: self.id,
|
user_id: self.id,
|
||||||
method: UserSecondFactor.methods[:totp],
|
method: UserSecondFactor.methods[:totp],
|
||||||
@ -14,6 +15,7 @@ module SecondFactorManager
|
|||||||
end
|
end
|
||||||
|
|
||||||
def get_totp_object(data)
|
def get_totp_object(data)
|
||||||
|
require_rotp
|
||||||
ROTP::TOTP.new(data, issuer: SiteSetting.title)
|
ROTP::TOTP.new(data, issuer: SiteSetting.title)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ module SecondFactorManager
|
|||||||
last_used = totp.last_used.to_i
|
last_used = totp.last_used.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
authenticated = !token.blank? && totp.get_totp_object.verify(
|
authenticated = !token.blank? && totp.totp_object.verify(
|
||||||
token,
|
token,
|
||||||
drift_ahead: TOTP_ALLOWED_DRIFT_SECONDS,
|
drift_ahead: TOTP_ALLOWED_DRIFT_SECONDS,
|
||||||
drift_behind: TOTP_ALLOWED_DRIFT_SECONDS,
|
drift_behind: TOTP_ALLOWED_DRIFT_SECONDS,
|
||||||
@ -132,4 +134,8 @@ module SecondFactorManager
|
|||||||
def hash_backup_code(code, salt)
|
def hash_backup_code(code, salt)
|
||||||
Pbkdf2.hash_password(code, salt, Rails.configuration.pbkdf2_iterations, Rails.configuration.pbkdf2_algorithm)
|
Pbkdf2.hash_password(code, salt, Rails.configuration.pbkdf2_iterations, Rails.configuration.pbkdf2_algorithm)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def require_rotp
|
||||||
|
require 'rotp' if !defined? ROTP
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UserSecondFactor < ActiveRecord::Base
|
class UserSecondFactor < ActiveRecord::Base
|
||||||
|
include SecondFactorManager
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
scope :backup_codes, -> do
|
scope :backup_codes, -> do
|
||||||
@ -22,12 +23,12 @@ class UserSecondFactor < ActiveRecord::Base
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_totp_object
|
def totp_object
|
||||||
ROTP::TOTP.new(self.data, issuer: SiteSetting.title)
|
get_totp_object(self.data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def totp_provisioning_uri
|
def totp_provisioning_uri
|
||||||
get_totp_object.provisioning_uri(user.email)
|
totp_object.provisioning_uri(user.email)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -18,8 +18,8 @@ RSpec.describe SecondFactorManager do
|
|||||||
totp = another_user.create_totp(enabled: true)
|
totp = another_user.create_totp(enabled: true)
|
||||||
end.to change { UserSecondFactor.count }.by(1)
|
end.to change { UserSecondFactor.count }.by(1)
|
||||||
|
|
||||||
expect(totp.get_totp_object.issuer).to eq(SiteSetting.title)
|
expect(totp.totp_object.issuer).to eq(SiteSetting.title)
|
||||||
expect(totp.get_totp_object.secret).to eq(another_user.reload.user_second_factors.totps.first.data)
|
expect(totp.totp_object.secret).to eq(another_user.reload.user_second_factors.totps.first.data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ RSpec.describe SecondFactorManager do
|
|||||||
freeze_time do
|
freeze_time do
|
||||||
expect(user.user_second_factors.totps.first.last_used).to eq(nil)
|
expect(user.user_second_factors.totps.first.last_used).to eq(nil)
|
||||||
|
|
||||||
token = user.user_second_factors.totps.first.get_totp_object.now
|
token = user.user_second_factors.totps.first.totp_object.now
|
||||||
|
|
||||||
expect(user.authenticate_totp(token)).to eq(true)
|
expect(user.authenticate_totp(token)).to eq(true)
|
||||||
expect(user.user_second_factors.totps.first.last_used).to eq_time(DateTime.now)
|
expect(user.user_second_factors.totps.first.last_used).to eq_time(DateTime.now)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
require 'rotp'
|
||||||
|
|
||||||
RSpec.describe SessionController do
|
RSpec.describe SessionController do
|
||||||
let(:email_token) { Fabricate(:email_token) }
|
let(:email_token) { Fabricate(:email_token) }
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
require 'rotp'
|
||||||
|
|
||||||
describe UsersController do
|
describe UsersController do
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
require 'rotp'
|
||||||
|
|
||||||
describe UsersEmailController do
|
describe UsersEmailController do
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user