This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/controllers/preferences/second-factor.js.es6
Jeff Wong 88ef5e55fe
FEATURE: add ability to have multiple totp factors (#7626)
Adds a second factor landing page that centralizes a user's second factor configuration.

This contains both TOTP and Backup, and also allows multiple TOTP tokens to be registered and organized by a name. Access to this page is authenticated via password, and cached for 30 minutes via a secure session.
2019-06-26 16:58:06 -07:00

176 lines
4.6 KiB
JavaScript

import { default as computed } from "ember-addons/ember-computed-decorators";
import CanCheckEmails from "discourse/mixins/can-check-emails";
import { default as DiscourseURL, userPath } from "discourse/lib/url";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { findAll } from "discourse/models/login-method";
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
import showModal from "discourse/lib/show-modal";
export default Ember.Controller.extend(CanCheckEmails, {
loading: false,
dirty: false,
resetPasswordLoading: false,
resetPasswordProgress: "",
password: null,
errorMessage: null,
newUsername: null,
backupEnabled: Ember.computed.alias("model.second_factor_backup_enabled"),
secondFactorMethod: SECOND_FACTOR_METHODS.TOTP,
totps: null,
loaded: Ember.computed.and("secondFactorImage", "secondFactorKey"),
init() {
this._super(...arguments);
this.set("totps", []);
},
@computed
displayOAuthWarning() {
return findAll().length > 0;
},
@computed("currentUser")
showEnforcedNotice(user) {
return user && user.enforcedSecondFactor;
},
handleError(error) {
if (error.jqXHR) {
error = error.jqXHR;
}
let parsedJSON = error.responseJSON;
if (parsedJSON.error_type === "invalid_access") {
const usernameLower = this.model.username.toLowerCase();
DiscourseURL.redirectTo(
userPath(`${usernameLower}/preferences/second-factor`)
);
} else {
popupAjaxError(error);
}
},
loadSecondFactors() {
if (this.dirty === false) {
return;
}
this.set("loading", true);
this.model
.loadSecondFactorCodes(this.password)
.then(response => {
if (response.error) {
this.set("errorMessage", response.error);
return;
}
this.setProperties({
errorMessage: null,
loaded: true,
totps: response.totps,
password: null,
dirty: false
});
this.set(
"model.second_factor_enabled",
response.totps && response.totps.length > 0
);
})
.catch(e => this.handleError(e))
.finally(() => this.set("loading", false));
},
markDirty() {
this.set("dirty", true);
},
actions: {
confirmPassword() {
if (!this.password) return;
this.markDirty();
this.loadSecondFactors();
this.set("password", null);
},
resetPassword() {
this.setProperties({
resetPasswordLoading: true,
resetPasswordProgress: ""
});
return this.model
.changePassword()
.then(() => {
this.set(
"resetPasswordProgress",
I18n.t("user.change_password.success")
);
})
.catch(popupAjaxError)
.finally(() => this.set("resetPasswordLoading", false));
},
disableAllSecondFactors() {
if (this.loading) {
return;
}
bootbox.confirm(
I18n.t("user.second_factor.disable_confirm"),
I18n.t("cancel"),
I18n.t("user.second_factor.disable"),
result => {
if (result) {
this.model
.disableAllSecondFactors()
.then(() => {
const usernameLower = this.model.username.toLowerCase();
DiscourseURL.redirectTo(
userPath(`${usernameLower}/preferences`)
);
})
.catch(e => this.handleError(e))
.finally(() => this.set("loading", false));
}
}
);
},
createTotp() {
const controller = showModal("second-factor-add-totp", {
model: this.model,
title: "user.second_factor.totp.add"
});
controller.setProperties({
onClose: () => this.loadSecondFactors(),
markDirty: () => this.markDirty(),
onError: e => this.handleError(e)
});
},
editSecondFactor(second_factor) {
const controller = showModal("second-factor-edit", {
model: second_factor,
title: "user.second_factor.edit_title"
});
controller.setProperties({
user: this.model,
onClose: () => this.loadSecondFactors(),
markDirty: () => this.markDirty(),
onError: e => this.handleError(e)
});
},
editSecondFactorBackup() {
const controller = showModal("second-factor-backup-edit", {
model: this.model,
title: "user.second_factor_backup.title"
});
controller.setProperties({
onClose: () => this.loadSecondFactors(),
markDirty: () => this.markDirty(),
onError: e => this.handleError(e)
});
}
}
});