Skip to content

Commit 43e57d3

Browse files
authored
Merge pull request #6125 from rubygems/reset-webauthn-admin
Reset webauthn devices in reset 2FA action
2 parents bccbc25 + 2c6ad2d commit 43e57d3

File tree

5 files changed

+20
-9
lines changed

5 files changed

+20
-9
lines changed

app/avo/actions/reset_user_2fa.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ class Avo::Actions::ResetUser2fa < Avo::Actions::ApplicationAction
55
}
66

77
self.message = lambda {
8-
"Are you sure you would like to disable MFA and reset the password for #{record.handle} #{record.email}?"
8+
"Are you sure you would like to disable MFA, WebAuthn devices, and reset the password for #{record.handle} #{record.email}?"
99
}
1010

1111
self.confirm_button_label = "Reset MFA"
1212

1313
class ActionHandler < Avo::Actions::ActionHandler
1414
def handle_record(user)
15-
user.disable_totp!
15+
user.disable_totp! if user.totp_enabled?
16+
user.webauthn_credentials.destroy_all if user.webauthn_enabled?
17+
user.reset_mfa_attributes
18+
1619
user.password = SecureRandom.hex(20).encode("UTF-8")
1720
user.save!
1821
end

app/models/concerns/user_multifactor_methods.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ def mfa_method_added(default_level)
7272
self.mfa_hashed_recovery_codes = new_mfa_recovery_codes.map { |code| BCrypt::Password.create(code) }
7373
end
7474

75+
def reset_mfa_attributes
76+
self.mfa_level = "disabled"
77+
self.new_mfa_recovery_codes = nil
78+
self.mfa_hashed_recovery_codes = []
79+
end
80+
7581
private
7682

7783
def strong_mfa_level?

app/models/concerns/user_totp_methods.rb

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ def totp_disabled?
1212
def disable_totp!
1313
self.totp_seed = nil
1414

15-
if no_mfa_devices?
16-
self.mfa_level = "disabled"
17-
self.mfa_hashed_recovery_codes = []
18-
end
15+
reset_mfa_attributes if no_mfa_devices?
1916

2017
save!(validate: false)
2118
Mailer.totp_disabled(id, Time.now.utc).deliver_later

app/models/webauthn_credential.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ def enable_user_mfa
2828

2929
def disable_user_mfa
3030
return unless user.no_mfa_devices?
31-
user.mfa_level = :disabled
32-
user.new_mfa_recovery_codes = nil
33-
user.mfa_hashed_recovery_codes = []
31+
user.reset_mfa_attributes
3432
user.save!(validate: false)
3533
end
3634
end

test/system/avo/users_test.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ class Avo::UsersSystemTest < ApplicationSystemTestCase
1212

1313
user = create(:user)
1414
user.enable_totp!(ROTP::Base32.random_base32, :ui_and_api)
15+
webauthn_credential = create(:webauthn_credential, user:)
1516
user_attributes = user.attributes.with_indifferent_access
17+
webauthn_attributes = webauthn_credential.attributes.with_indifferent_access
1618

1719
visit avo.resources_user_path(user)
1820

@@ -41,6 +43,7 @@ class Avo::UsersSystemTest < ApplicationSystemTestCase
4143
assert_not_equal user_attributes[:encrypted_password], user.encrypted_password
4244
assert_nil user.totp_seed
4345
assert_empty user.mfa_hashed_recovery_codes
46+
assert_empty user.webauthn_credentials
4447

4548
audit = user.audits.sole
4649
event = user.events.where(tag: Events::UserEvent::PASSWORD_CHANGED).sole
@@ -64,6 +67,10 @@ class Avo::UsersSystemTest < ApplicationSystemTestCase
6467
.except("mfa_level", "updated_at", "totp_seed", "mfa_hashed_recovery_codes", "encrypted_password")
6568
.transform_values(&:as_json)
6669
},
70+
"gid://gemcutter/WebauthnCredential/#{webauthn_credential.id}" => {
71+
"changes" => webauthn_attributes.transform_values { [it.as_json, nil] },
72+
"unchanged" => {}
73+
},
6774
event.to_gid.as_json => {
6875
"changes" => event.attributes.transform_values { [nil, it.as_json] },
6976
"unchanged" => {}

0 commit comments

Comments
 (0)