DEV: Show confirmation dialog when admins disable 2FA (#29652)

This PR ensures that admins are shown a confirmation dialog when clicking to disable 2FA for a user. The 2FA button is right below the "Grant Badge" button and as such it can easily be clicked accidentally. It's also good practice to ask for confirmation before removing important functionality.
This commit is contained in:
Keegan George 2024-11-08 09:39:42 +09:00 committed by GitHub
parent fc5c0270f7
commit 5a23a74bbc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 46 additions and 6 deletions

View File

@ -374,7 +374,12 @@ export default class AdminUserIndexController extends Controller.extend(
@action @action
disableSecondFactor() { disableSecondFactor() {
return this.model.disableSecondFactor(); this.dialog.yesNoConfirm({
message: I18n.t("admin.user.disable_second_factor_confirm"),
didConfirm: () => {
return this.model.disableSecondFactor();
},
});
} }
@action @action

View File

@ -230,7 +230,7 @@
</div> </div>
{{/if}} {{/if}}
<div class="display-row"> <div class="display-row second-factor">
<div class="field">{{i18n "user.second_factor.title"}}</div> <div class="field">{{i18n "user.second_factor.title"}}</div>
<div class="value"> <div class="value">
{{#if this.model.second_factor_enabled}} {{#if this.model.second_factor_enabled}}
@ -245,7 +245,7 @@
@action={{this.disableSecondFactor}} @action={{this.disableSecondFactor}}
@icon="unlock-keyhole" @icon="unlock-keyhole"
@label="user.second_factor.disable" @label="user.second_factor.disable"
class="btn-default" class="btn-default disable-second-factor"
/> />
{{/if}} {{/if}}
</div> </div>

View File

@ -81,6 +81,24 @@ acceptance("Admin - User Index", function (needs) {
}); });
}); });
server.get("/admin/users/7.json", () => {
return helper.response({
id: 7,
username: "jimmy",
name: "Jimmy Johnson",
avatar_template: "/letter_avatar_proxy/v4/letter/b/f0a364/{size}.png",
active: true,
admin: false,
moderator: false,
can_grant_admin: true,
can_revoke_admin: false,
can_grant_moderation: true,
can_revoke_moderation: false,
second_factor_enabled: true,
can_disable_second_factor: true,
});
});
server.put("/admin/users/4/grant_admin", () => { server.put("/admin/users/4/grant_admin", () => {
return helper.response(403, { return helper.response(403, {
second_factor_challenge_nonce: "some-nonce", second_factor_challenge_nonce: "some-nonce",
@ -140,6 +158,10 @@ acceptance("Admin - User Index", function (needs) {
html_message: true, html_message: true,
}); });
}); });
server.put("/admin/users/7/disable_second_factor", () => {
return helper.response({ success: "OK" });
});
}); });
needs.hooks.beforeEach(() => { needs.hooks.beforeEach(() => {
@ -255,6 +277,18 @@ acceptance("Admin - User Index", function (needs) {
); );
}); });
test("disable 2fa - shows the confirmation dialog", async function (assert) {
await visit("/admin/users/7/jimmy");
await click(".disable-second-factor");
assert.dom(".dialog-content").exists();
assert.strictEqual(
I18n.t("admin.user.disable_second_factor_confirm"),
query(".dialog-body").textContent.trim()
);
await click(".dialog-footer .btn-primary");
});
test("delete user - delete without blocking works as expected", async function (assert) { test("delete user - delete without blocking works as expected", async function (assert) {
await visit("/admin/users/5/user5"); await visit("/admin/users/5/user5");
await click(".btn-user-delete"); await click(".btn-user-delete");

View File

@ -6770,8 +6770,9 @@ en:
time_read: "Read Time" time_read: "Read Time"
post_edits_count: "Post Edits" post_edits_count: "Post Edits"
anonymize: "Anonymize User" anonymize: "Anonymize User"
anonymize_confirm: "Are you SURE you want to anonymize this account? This will change the username and email, and reset all profile information." anonymize_confirm: "Are you sure you want to anonymize this account? This will change the username and email, and reset all profile information."
delete_associated_accounts_confirm: "Are you SURE you want to delete all associated accounts from this account? The user may not be able to log in if their email has changed." delete_associated_accounts_confirm: "Are you sure you want to delete all associated accounts from this account? The user may not be able to log in if their email has changed."
disable_second_factor_confirm: "Are you sure you want to disable Two-Factor Authentication for this account?"
anonymize_yes: "Yes, anonymize this account" anonymize_yes: "Yes, anonymize this account"
anonymize_failed: "There was a problem anonymizing the account." anonymize_failed: "There was a problem anonymizing the account."
delete: "Delete User" delete: "Delete User"
@ -6832,7 +6833,7 @@ en:
cant_delete_all_too_many_posts: cant_delete_all_too_many_posts:
one: "Can't delete all posts because the user has more than %{count} post. (delete_all_posts_max)" one: "Can't delete all posts because the user has more than %{count} post. (delete_all_posts_max)"
other: "Can't delete all posts because the user has more than %{count} posts. (delete_all_posts_max)" other: "Can't delete all posts because the user has more than %{count} posts. (delete_all_posts_max)"
delete_confirm_title: "Are you SURE you want to delete this user? This is permanent!" delete_confirm_title: "Are you sure you want to delete this user? This is permanent!"
delete_confirm: "It is generally preferable to anonymize users rather than deleting them, to avoid removing content from existing discussions." delete_confirm: "It is generally preferable to anonymize users rather than deleting them, to avoid removing content from existing discussions."
delete_and_block: "Delete and <b>block</b> this email and IP address" delete_and_block: "Delete and <b>block</b> this email and IP address"
delete_dont_block: "Delete only" delete_dont_block: "Delete only"