diff options
author | Go MAEDA <maeda@farend.jp> | 2020-08-29 06:51:21 +0000 |
---|---|---|
committer | Go MAEDA <maeda@farend.jp> | 2020-08-29 06:51:21 +0000 |
commit | 8900eb6eb5994310e3f957398cc21a512c5951ab (patch) | |
tree | 22c37392fef2ef6f750dbd9bda18c90c6cfd45b9 /lib/redmine/twofa | |
parent | be7f5e21faa05bdc483d1b58c8887ff499082073 (diff) | |
download | redmine-8900eb6eb5994310e3f957398cc21a512c5951ab.tar.gz redmine-8900eb6eb5994310e3f957398cc21a512c5951ab.zip |
Backup codes for 2fa auth (#1237).
Patch by Felix Schäfer.
git-svn-id: http://svn.redmine.org/redmine/trunk@19990 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'lib/redmine/twofa')
-rw-r--r-- | lib/redmine/twofa/base.rb | 50 |
1 files changed, 48 insertions, 2 deletions
diff --git a/lib/redmine/twofa/base.rb b/lib/redmine/twofa/base.rb index 8369c8f6a..e959aa930 100644 --- a/lib/redmine/twofa/base.rb +++ b/lib/redmine/twofa/base.rb @@ -42,7 +42,7 @@ module Redmine end def confirm_pairing!(code) - # make sure an otp is used + # make sure an otp and not a backup code is used if verify_otp!(code) @user.update!(twofa_scheme: scheme_name) deliver_twofa_paired @@ -77,6 +77,7 @@ module Redmine def destroy_pairing_without_verify! @user.update!(twofa_scheme: nil) + backup_codes.delete_all deliver_twofa_unpaired end @@ -98,13 +99,58 @@ module Redmine end def verify!(code) - verify_otp!(code) + verify_otp!(code) || verify_backup_code!(code) end def verify_otp!(code) raise 'not implemented' end + def verify_backup_code!(code) + # backup codes are case-insensitive and white-space-insensitive + code = code.to_s.remove(/[[:space:]]/).downcase + user_from_code = Token.find_active_user('twofa_backup_code', code) + # invalidate backup code after usage + Token.where(user_id: @user.id).find_token('twofa_backup_code', code).try(:delete) + # make sure the user using the backup code is the same it's been issued to + return false unless @user.present? && @user == user_from_code + Mailer.security_notification( + @user, + User.current, + { + originator: @user, + title: :label_my_account, + message: 'twofa_mail_body_backup_code_used', + url: { controller: 'my', action: 'account' } + } + ).deliver + return true + end + + def init_backup_codes! + backup_codes.delete_all + tokens = [] + 10.times do + token = Token.create(user_id: @user.id, action: 'twofa_backup_code') + token.update_columns value: Redmine::Utils.random_hex(6) + tokens << token + end + Mailer.security_notification( + @user, + User.current, + { + title: :label_my_account, + message: 'twofa_mail_body_backup_codes_generated', + url: { controller: 'my', action: 'account' } + } + ).deliver + tokens + end + + def backup_codes + Token.where(user_id: @user.id, action: 'twofa_backup_code') + end + # this will only be used on pairing initialization def init_pairing_view_variables otp_confirm_view_variables |