From 80ac3275ba057ce76aaf82c6898474142496831c Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Tue, 15 Oct 2024 18:11:29 -0400 Subject: [PATCH] DEV: update rake task to disable 2FA for a user (#29052) - limits security key deletes to second factor keys - also deletes backup codes (lingering backup codes break login flow entirely) * Add spec for rake task to disable 2FA for a user --- lib/tasks/users.rake | 8 +++++++- spec/tasks/users_spec.rb | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 spec/tasks/users_spec.rb diff --git a/lib/tasks/users.rake b/lib/tasks/users.rake index 3df8ad8bd8a..6d6a991185c 100644 --- a/lib/tasks/users.rake +++ b/lib/tasks/users.rake @@ -155,7 +155,13 @@ task "users:disable_2fa", [:username] => [:environment] do |_, args| username = args[:username] user = find_user(username) UserSecondFactor.where(user_id: user.id, method: UserSecondFactor.methods[:totp]).each(&:destroy!) - UserSecurityKey.where(user_id: user.id).destroy_all + UserSecurityKey.where( + user_id: user.id, + factor_type: UserSecurityKey.factor_types[:second_factor], + ).destroy_all + UserSecondFactor.where(user_id: user.id, method: UserSecondFactor.methods[:backup_codes]).each( + &:destroy! + ) puts "2FA disabled for #{username}" end diff --git a/spec/tasks/users_spec.rb b/spec/tasks/users_spec.rb new file mode 100644 index 00000000000..98df100cc08 --- /dev/null +++ b/spec/tasks/users_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +RSpec.describe "tasks/users" do + before do + Rake::Task.clear + Discourse::Application.load_tasks + end + + describe "users:disable_2fa" do + let(:user) { Fabricate(:user) } + + it "should remove all 2fa methods for user with given username" do + Fabricate(:user_second_factor_totp, user: user, name: "TOTP", enabled: true) + Fabricate(:user_second_factor_totp, user: user, name: "TOTP2", enabled: true) + Fabricate( + :user_security_key_with_random_credential, + user: user, + name: "YubiKey", + enabled: true, + ) + Fabricate(:passkey_with_random_credential, user: user) # This should not be removed + + backup_codes = user.generate_backup_codes + + expect(backup_codes.length).to be 10 + expect(user.user_second_factors.backup_codes).to be_present + expect(user.user_second_factors.totps.count).to eq(2) + expect(user.second_factor_security_keys.count).to eq(1) + + stdout = capture_stdout { Rake::Task["users:disable_2fa"].invoke(user.username) } + user.reload + + expect(stdout.chomp).to eq("2FA disabled for #{user.username}") + expect(user.user_second_factors.totps.count).to eq(0) + expect(user.second_factor_security_keys.count).to eq(0) + expect(user.user_second_factors.backup_codes.count).to eq(0) + expect(user.passkey_credential_ids.count).to eq(1) + end + end +end