DEV: Convert security-key-form to glimmer/gjs (#27364)

(`allowedCredentialIds` and `challenge` args were unused)
This commit is contained in:
Jarek Radosz 2024-06-10 15:06:48 +02:00 committed by GitHub
parent 42a529f9ae
commit c18e5d1698
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 172 additions and 176 deletions

View File

@ -78,11 +78,9 @@
>
{{#if @showSecurityKey}}
<SecurityKeyForm
@allowedCredentialIds={{@securityKeyAllowedCredentialIds}}
@challenge={{@securityKeyChallenge}}
@showSecurityKey={{@showSecurityKey}}
@showSecondFactor={{@showSecondFactor}}
@secondFactorMethod={{@secondFactorMethod}}
@setShowSecurityKey={{fn (mut @showSecurityKey)}}
@setShowSecondFactor={{fn (mut @showSecondFactor)}}
@setSecondFactorMethod={{fn (mut @secondFactorMethod)}}
@backupEnabled={{@backupEnabled}}
@totpEnabled={{@totpEnabled}}
@otherMethodAllowed={{@otherMethodAllowed}}

View File

@ -0,0 +1,42 @@
import Component from "@glimmer/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import DButton from "discourse/components/d-button";
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
import i18n from "discourse-common/helpers/i18n";
export default class SecurityKeyForm extends Component {
@action
useAnotherMethod(event) {
event.preventDefault();
this.args.setShowSecurityKey?.(false);
this.args.setShowSecondFactor?.(true);
if (this.args.totpEnabled) {
this.args.setSecondFactorMethod?.(SECOND_FACTOR_METHODS.TOTP);
} else if (this.args.backupEnabled) {
this.args.setSecondFactorMethod?.(SECOND_FACTOR_METHODS.BACKUP_CODE);
}
}
<template>
<div id="security-key">
<DButton
@action={{@action}}
@icon="key"
@label="login.security_key_authenticate"
id="security-key-authenticate-button"
class="btn-large btn-primary"
/>
<p>
{{#if @otherMethodAllowed}}
<a
{{on "click" this.useAnotherMethod}}
href
class="toggle-second-factor-method"
>{{i18n "login.security_key_alternative"}}</a>
{{/if}}
</p>
</div>
</template>
}

View File

@ -1,18 +0,0 @@
<div id="security-key">
<DButton
@action={{this.action}}
@icon="key"
@label="login.security_key_authenticate"
id="security-key-authenticate-button"
class="btn-large btn-primary"
/>
<p>
{{#if this.otherMethodAllowed}}
<a
href
class="toggle-second-factor-method"
{{on "click" this.useAnotherMethod}}
>{{i18n "login.security_key_alternative"}}</a>
{{/if}}
</p>
</div>

View File

@ -1,18 +0,0 @@
import Component from "@ember/component";
import { action } from "@ember/object";
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
export default Component.extend({
@action
useAnotherMethod(event) {
event?.preventDefault();
this.set("showSecurityKey", false);
this.set("showSecondFactor", true);
if (this.totpEnabled) {
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP);
} else if (this.backupEnabled) {
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.BACKUP_CODE);
}
},
});

View File

@ -1,4 +1,5 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
@ -11,9 +12,7 @@ export default Controller.extend({
router: service(),
secondFactorMethod: null,
secondFactorToken: null,
lockImageUrl: getURL("/images/lock.svg"),
@discourseComputed("model")
@ -21,57 +20,62 @@ export default Controller.extend({
return model.security_key_required || model.second_factor_required;
},
actions: {
finishLogin() {
let data = {
second_factor_method: this.secondFactorMethod,
timezone: moment.tz.guess(),
};
if (this.securityKeyCredential) {
data.second_factor_token = this.securityKeyCredential;
} else {
data.second_factor_token = this.secondFactorToken;
}
@action
async finishLogin() {
let data = {
second_factor_method: this.secondFactorMethod,
timezone: moment.tz.guess(),
};
ajax({
if (this.securityKeyCredential) {
data.second_factor_token = this.securityKeyCredential;
} else {
data.second_factor_token = this.secondFactorToken;
}
try {
const result = await ajax({
url: `/session/email-login/${this.model.token}`,
type: "POST",
data,
})
.then((result) => {
if (result.success) {
let destination = "/";
});
const safeMode = new URL(
this.router.currentURL,
window.location.origin
).searchParams.get("safe_mode");
if (!result.success) {
this.set("model.error", result.error);
return;
}
if (safeMode) {
const params = new URLSearchParams();
params.set("safe_mode", safeMode);
destination += `?${params.toString()}`;
}
let destination = "/";
DiscourseURL.redirectTo(destination);
} else {
this.set("model.error", result.error);
}
})
.catch(popupAjaxError);
},
authenticateSecurityKey() {
getWebauthnCredential(
this.model.challenge,
this.model.allowed_credential_ids,
(credentialData) => {
this.set("securityKeyCredential", credentialData);
this.send("finishLogin");
},
(errorMessage) => {
this.set("model.error", errorMessage);
}
);
},
const safeMode = new URL(
this.router.currentURL,
window.location.origin
).searchParams.get("safe_mode");
if (safeMode) {
const params = new URLSearchParams();
params.set("safe_mode", safeMode);
destination += `?${params.toString()}`;
}
DiscourseURL.redirectTo(destination);
} catch (e) {
popupAjaxError(e);
}
},
@action
authenticateSecurityKey() {
getWebauthnCredential(
this.model.challenge,
this.model.allowed_credential_ids,
(credentialData) => {
this.set("securityKeyCredential", credentialData);
this.send("finishLogin");
},
(errorMessage) => {
this.set("model.error", errorMessage);
}
);
},
});

View File

@ -79,9 +79,10 @@ export default Controller.extend(PasswordValidation, {
this.toggleProperty("maskPassword");
},
actions: {
submit() {
ajax({
@action
async submit() {
try {
const result = await ajax({
url: userPath(`password-reset/${this.get("model.token")}.json`),
type: "PUT",
data: {
@ -91,81 +92,71 @@ export default Controller.extend(PasswordValidation, {
second_factor_method: this.selectedSecondFactorMethod,
timezone: moment.tz.guess(),
},
})
.then((result) => {
if (result.success) {
this.set("successMessage", result.message);
this.set("redirectTo", result.redirect_to);
if (result.requires_approval) {
this.set("requiresApproval", true);
} else {
this.set("redirected", true);
DiscourseURL.redirectTo(result.redirect_to || "/");
}
} else {
if (
result.errors.security_keys ||
result.errors.user_second_factors
) {
this.setProperties({
secondFactorRequired: this.secondFactorRequired,
securityKeyRequired: this.securityKeyRequired,
password: null,
errorMessage: result.message,
});
} else if (this.secondFactorRequired || this.securityKeyRequired) {
this.setProperties({
secondFactorRequired: false,
securityKeyRequired: false,
errorMessage: null,
});
} else if (
result.errors &&
result.errors.password &&
result.errors.password.length > 0
) {
this.rejectedPasswords.pushObject(this.accountPassword);
this.rejectedPasswordsMessages.set(
this.accountPassword,
(result.friendly_messages || []).join("\n")
);
}
});
if (result.message) {
this.set("errorMessage", result.message);
}
}
})
.catch((e) => {
if (e.jqXHR && e.jqXHR.status === 429) {
this.set("errorMessage", I18n.t("user.second_factor.rate_limit"));
} else {
throw new Error(e);
}
});
},
if (result.success) {
this.set("successMessage", result.message);
this.set("redirectTo", result.redirect_to);
authenticateSecurityKey() {
this.set(
"selectedSecondFactorMethod",
SECOND_FACTOR_METHODS.SECURITY_KEY
);
getWebauthnCredential(
this.model.challenge,
this.model.allowed_credential_ids,
(credentialData) => {
this.set("securityKeyCredential", credentialData);
this.send("submit");
},
(errorMessage) => {
this.setProperties({
securityKeyRequired: true,
password: null,
errorMessage,
});
if (result.requires_approval) {
this.set("requiresApproval", true);
} else {
this.set("redirected", true);
DiscourseURL.redirectTo(result.redirect_to || "/");
}
);
},
} else {
if (result.errors.security_keys || result.errors.user_second_factors) {
this.setProperties({
secondFactorRequired: this.secondFactorRequired,
securityKeyRequired: this.securityKeyRequired,
password: null,
errorMessage: result.message,
});
} else if (this.secondFactorRequired || this.securityKeyRequired) {
this.setProperties({
secondFactorRequired: false,
securityKeyRequired: false,
errorMessage: null,
});
} else if (result.errors?.password?.length > 0) {
this.rejectedPasswords.pushObject(this.accountPassword);
this.rejectedPasswordsMessages.set(
this.accountPassword,
(result.friendly_messages || []).join("\n")
);
}
if (result.message) {
this.set("errorMessage", result.message);
}
}
} catch (e) {
if (e.jqXHR?.status === 429) {
this.set("errorMessage", I18n.t("user.second_factor.rate_limit"));
} else {
throw new Error(e);
}
}
},
@action
authenticateSecurityKey() {
this.set("selectedSecondFactorMethod", SECOND_FACTOR_METHODS.SECURITY_KEY);
getWebauthnCredential(
this.model.challenge,
this.model.allowed_credential_ids,
(credentialData) => {
this.set("securityKeyCredential", credentialData);
this.send("submit");
},
(errorMessage) => {
this.setProperties({
securityKeyRequired: true,
password: null,
errorMessage,
});
}
);
},
});

View File

@ -16,15 +16,14 @@
{{#if this.secondFactorRequired}}
{{#if this.model.security_key_required}}
<SecurityKeyForm
@allowedCredentialIds={{this.model.allowed_credential_ids}}
@challenge={{this.model.security_key_challenge}}
@showSecurityKey={{this.model.security_key_required}}
@showSecondFactor={{false}}
@secondFactorMethod={{this.secondFactorMethod}}
@setShowSecurityKey={{fn
(mut this.model.security_key_required)
}}
@setSecondFactorMethod={{fn (mut this.secondFactorMethod)}}
@backupEnabled={{this.model.backup_codes_enabled}}
@totpEnabled={{this.model.totp_enabled}}
@otherMethodAllowed={{this.secondFactorRequired}}
@action={{action "authenticateSecurityKey"}}
@action={{this.authenticateSecurityKey}}
/>
{{else}}
<SecondFactorForm

View File

@ -35,15 +35,13 @@
{{#if this.displaySecurityKeyForm}}
<SecurityKeyForm
@allowedCredentialIds={{this.model.allowed_credential_ids}}
@challenge={{this.model.security_key_challenge}}
@showSecurityKey={{false}}
@showSecondFactor={{false}}
@secondFactorMethod={{this.selectedSecondFactorMethod}}
@setSecondFactorMethod={{fn
(mut this.selectedSecondFactorMethod)
}}
@backupEnabled={{this.backupEnabled}}
@totpEnabled={{this.secondFactorRequired}}
@otherMethodAllowed={{this.otherMethodAllowed}}
@action={{action "authenticateSecurityKey"}}
@action={{this.authenticateSecurityKey}}
/>
{{else}}
<SecondFactorForm