diff --git a/src/components/user_settings/mfa.js b/src/components/user_settings/mfa.js index 43c39921d4..4ba9a564c9 100644 --- a/src/components/user_settings/mfa.js +++ b/src/components/user_settings/mfa.js @@ -1,131 +1,134 @@ import RecoveryCodes from './mfa_backup_codes.vue' - +import TOTP from './mfa_totp.vue' const Mfa = { data () { return { - settings: {}, // current settings of MFA + settings: { // current settings of MFA + enabled: false, + totp: false + }, + setupState: { // setup mfa + state: '', // state of setup. '' -> 'getBackupCodes' -> 'setupOTP' -> 'complete' + setupOTPState: '' // state of setup otp. '' -> 'prepare' -> 'confirm' -> 'complete' + }, + backupCodes: { + inProgress: false, // progress of fetch codes + codes: [] + }, currentPassword: null, otpConfirmToken: null, - backup_codes: [], otpSettings: {}, // pre-setup setting of OTP. secret key, qrcode url. - performSetup: null, // step of setup. null -> getBackupCodes -> readyBackupCodes - performSetupOTP: null, // steps of setup otp. null -> init -> confirm - disableMethod: null, // disable method MFA - performGenerateNewCodes: false, - disableError: null, - confirmOTPError: null, + error: null, readyInit: false } }, - components: {'recovery-codes': RecoveryCodes}, + components: {'recovery-codes': RecoveryCodes, 'totp-item': TOTP}, computed: { canEnable () { - return !this.settings.enabled && this.performSetup === null - }, - showBackupCodes () { - return this.waitBackupCodes || this.readyBackupCodes - }, - waitBackupCodes () { - return this.performSetup === 'getBackupCodes' - }, - readyBackupCodes () { - return this.performSetup === 'readyBackupCodes' + return !this.settings.enabled && !this.setupInProgress }, canSetupOTP () { return ( - this.readyBackupCodes || this.settings.enabled + (this.setupInProgress && this.backupCodesPrepared) || + this.settings.enabled ) && this.settings.totp === false }, waitPreSetupOTP () { return this.performSetupOTP === null || this.performSetupOTP === 'init' + }, + disableOTP () { + return this.disableMethod === 'otp' + }, + setupInProgress () { + return this.setupState.state !== '' && this.setupState.state !== 'complete' + }, + prepareOTP () { + return this.setupState.setupOTPState === 'prepare' + }, + confirmOTP () { + return this.setupState.setupOTPState === 'confirm' + }, + backupCodesPrepared () { + return this.backupCodes.inProgress === false && this.backupCodes.codes.length > 0 } }, + methods: { enableMFA () { - this.performSetup = 'getBackupCodes' - this.backupCodes().then((codes) => { - this.backup_codes = codes - this.performSetup = 'readyBackupCodes' + this.setupState.state = 'getBackupcodes' + this.$nextTick(() => { + this.startFetchBackupCodes() + this.$store.state.api.backendInteractor.generateMfaBackupCodes() + .then((res) => this.setBackupCodes(res.codes)) + .then((res) => { this.setupState.state = 'setupOTP' }) }) }, // gets New Backup codes - getBackupCodes () { - this.performGenerateNewCodes = true - this.backupCodes().then((codes) => { - this.backup_codes = codes - this.performGenerateNewCodes = false + getNewBackupCodes () { + this.$nextTick(() => { + this.startFetchBackupCodes() + this.$store.state.api.backendInteractor.generateMfaBackupCodes().then( + (res) => this.setBackupCodes(res.codes) + ) }) }, - - backupCodes () { - return new Promise((resolve, reject) => { - this.$store.state.api.backendInteractor.generateMfaBackupCodes({}) - .then((res) => { resolve(res.codes) }) - }) + startFetchBackupCodes () { + this.backupCodes.inProgress = true + this.backupCodes.codes = [] + }, + setBackupCodes (codes) { + this.backupCodes.codes = codes + this.backupCodes.inProgress = false }, // Setup OTP - setupOTP () { - this.performSetupOTP = 'init' + setupOTP () { // prepare setup OTP + this.setupState.setupOTPState = 'prepare' this.$store.state.api.backendInteractor.mfaSetupOTP({}) .then((res) => { this.otpSettings = res - this.performSetupOTP = 'confirm' + this.setupState.setupOTPState = 'confirm' }) }, - confirmOTP () { - this.confirmOTPError = null + doConfirmOTP () { // handler confirm enable OTP + this.error = null this.$store.state.api.backendInteractor.mfaConfirmOTP({ token: this.otpConfirmToken, password: this.currentPassword }) .then((res) => { if (res.error) { - this.confirmOTPError = res.error + this.error = res.error return } - this.performSetup = null - this.currentPassword = null - this.fetchSettings(true) + this.completeSetup() }) }, + + completeSetup () { + this.setupState.setupOTPState = 'complete' + this.setupState.state = 'complete' + this.currentPassword = null + this.fetchSettings() + }, + // end Setup OTP - // disable OTP - handleDisableOTP () { - this.disableError = null - this.$store.state.api.backendInteractor.mfaDisableOTP({ - password: this.currentPassword - }) - .then((res) => { - if (res.error) { - this.disableError = res.error - return - } - this.disableMethod = null - this.currentPassword = null - this.fetchSettings(true) - }) - }, - // end disable OTP - // fetch settings from server - fetchSettings (force) { - if (force || this.settings.enabled === undefined) { - this.$store.state.api.backendInteractor.fetchSettingsMFA({}) - .then((res) => { - this.settings = res.settings - if (!this.readyInit) { - this.readyInit = true - } - }) - } + fetchSettings () { + this.$store.state.api.backendInteractor.fetchSettingsMFA() + .then((res) => { + this.settings = res.settings + if (!this.readyInit) { + this.readyInit = true + } + }) } // end fetch settings }, mounted () { - this.fetchSettings(true) + this.fetchSettings() } } export default Mfa diff --git a/src/components/user_settings/mfa.vue b/src/components/user_settings/mfa.vue index 3dd275c1c4..f86d3d868e 100644 --- a/src/components/user_settings/mfa.vue +++ b/src/components/user_settings/mfa.vue @@ -8,21 +8,19 @@ -
- +
+

{{$t('settings.mfa.recovery_codes')}}

+
- - + {{$t('settings.mfa.wait_pre_setup_otp')}} -
+

{{$t('settings.mfa.setup_otp')}}

{{$t('settings.mfa.scan.title')}}

{{$t('settings.mfa.scan.desc')}}

@@ -40,10 +38,10 @@

{{$t('settings.enter_current_password_to_confirm')}}:

- -
{{confirmOTPError}}
+
{{error}}
@@ -53,44 +51,23 @@

{{$t('settings.mfa.methods')}}

- -
- {{$t('settings.mfa.otp')}} - -
- -
- {{$t('settings.enter_current_password_to_confirm')}}: - - - -
{{disableError}}
-
- +
+

{{$t('settings.mfa.warning_of_generate_new_codes')}}

- - - +

{{$t('settings.mfa.recovery_codes')}}

+
diff --git a/src/components/user_settings/mfa_backup_codes.js b/src/components/user_settings/mfa_backup_codes.js index 3ebd957a91..a0a6b789a3 100644 --- a/src/components/user_settings/mfa_backup_codes.js +++ b/src/components/user_settings/mfa_backup_codes.js @@ -1,21 +1,18 @@ export default { props: { - progress: {type: Boolean, default: false}, - codes: { - type: Array, - default: () => { return [] } + backupCodes: { + type: Object, + default: () => ({ + inProgress: false, + codes: [] + }) } }, - data () { return { title: this.progress } }, + data: () => ({}), computed: { - ready () { return this.codes.length > 0 } - }, - mounted () { - let unwatch = this.$watch('progress', function (value) { - if (value) { - this.title = true - unwatch() - } - }) + inProgress () { + return this.backupCodes.inProgress + }, + ready () { return this.backupCodes.codes.length > 0 } } } diff --git a/src/components/user_settings/mfa_backup_codes.vue b/src/components/user_settings/mfa_backup_codes.vue index c5de44637c..4174a54924 100644 --- a/src/components/user_settings/mfa_backup_codes.vue +++ b/src/components/user_settings/mfa_backup_codes.vue @@ -1,16 +1,18 @@ diff --git a/src/components/user_settings/mfa_totp.js b/src/components/user_settings/mfa_totp.js new file mode 100644 index 0000000000..59f29c52b8 --- /dev/null +++ b/src/components/user_settings/mfa_totp.js @@ -0,0 +1,30 @@ +export default { + data: () => ({ + error: false, + currentPassword: '', + disable: false, + inProgress: false + }), + methods: { + doCancel () { this.disable = false }, + doDisable () { + this.error = null + this.disable = true + }, + confirmDisable () { + this.error = null + this.inProgress = true + this.$store.state.api.backendInteractor.mfaDisableOTP({ + password: this.currentPassword + }) + .then((res) => { + this.inProgress = false + if (res.error) { + this.error = res.error + return + } + this.$emit('disable') + }) + } + } +} diff --git a/src/components/user_settings/mfa_totp.vue b/src/components/user_settings/mfa_totp.vue new file mode 100644 index 0000000000..4b74b84f50 --- /dev/null +++ b/src/components/user_settings/mfa_totp.vue @@ -0,0 +1,23 @@ + + diff --git a/src/i18n/en.json b/src/i18n/en.json index 96fb3fa3d9..40f5ebce72 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -160,7 +160,7 @@ "otp" : "OTP", "setup_otp" : "Setup OTP", "wait_pre_setup_otp" : "waiting pre-setup OTP", - "confirm_and_enabled" : "Confirm & enable OTP", + "confirm_and_enable" : "Confirm & enable OTP", "title": "Two-factor Authentication", "generate_new_recovery_codes" : "Generate new recovery codes", "generate_recovery_codes" : "Generate recovery codes.", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 93412620af..2666689b06 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -100,7 +100,7 @@ "mfa": { "otp" : "OTP", "setup_otp" : "Настройка OTP", - "confirm_and_enabled" : "Подтвердить и включить OTP", + "confirm_and_enable" : "Подтвердить и включить OTP", "title": "Двухфакторная аутентификация", "generate_new_recovery_codes" : "Получить новые коды востановления", "generate_recovery_codes" : "Коды восстановления",