Revert "add TOTP/Recovery Form for mobile version"

This reverts commit a3811f944819430c278b6da6b08dc322a9b9ff65.
This commit is contained in:
Maksim 2019-06-12 20:16:55 +00:00 committed by HJ
parent 9df99c5205
commit 77eceedbf7
32 changed files with 1657 additions and 439 deletions

View file

@ -0,0 +1,9 @@
const Confirm = {
props: ['disabled'],
data: () => ({}),
methods: {
confirm () { this.$emit('confirm') },
cancel () { this.$emit('cancel') }
}
}
export default Confirm

View file

@ -0,0 +1,14 @@
<template>
<div>
<slot></slot>
<button class="btn btn-default" @click="confirm" :disabled="disabled">
{{$t('general.confirm')}}
</button>
<button class="btn btn-default" @click="cancel" :disabled="disabled">
{{$t('general.cancel')}}
</button>
</div>
</template>
<script src="./confirm.js">
</script>

View file

@ -0,0 +1,152 @@
import RecoveryCodes from './mfa_backup_codes.vue'
import TOTP from './mfa_totp.vue'
import Confirm from './confirm.vue'
import VueQrcode from '@chenfengyuan/vue-qrcode'
import { mapState } from 'vuex'
const Mfa = {
data: () => ({
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: {
getNewCodes: false,
inProgress: false, // progress of fetch codes
codes: []
},
otpSettings: { // pre-setup setting of OTP. secret key, qrcode url.
provisioning_uri: '',
key: ''
},
currentPassword: null,
otpConfirmToken: null,
error: null,
readyInit: false
}),
components: {
'recovery-codes': RecoveryCodes,
'totp-item': TOTP,
'qrcode': VueQrcode,
'confirm': Confirm
},
computed: {
canSetupOTP () {
return (
(this.setupInProgress && this.backupCodesPrepared) ||
this.settings.enabled
) && !this.settings.totp && !this.setupOTPInProgress
},
setupInProgress () {
return this.setupState.state !== '' && this.setupState.state !== 'complete'
},
setupOTPInProgress () {
return this.setupState.state === 'setupOTP' && !this.completedOTP
},
prepareOTP () {
return this.setupState.setupOTPState === 'prepare'
},
confirmOTP () {
return this.setupState.setupOTPState === 'confirm'
},
completedOTP () {
return this.setupState.setupOTPState === 'completed'
},
backupCodesPrepared () {
return !this.backupCodes.inProgress && this.backupCodes.codes.length > 0
},
confirmNewBackupCodes () {
return this.backupCodes.getNewCodes
},
...mapState({
backendInteractor: (state) => state.api.backendInteractor
})
},
methods: {
activateOTP () {
if (!this.settings.enabled) {
this.setupState.state = 'getBackupcodes'
this.fetchBackupCodes()
}
},
fetchBackupCodes () {
this.backupCodes.inProgress = true
this.backupCodes.codes = []
return this.backendInteractor.generateMfaBackupCodes()
.then((res) => {
this.backupCodes.codes = res.codes
this.backupCodes.inProgress = false
})
},
getBackupCodes () { // get a new backup codes
this.backupCodes.getNewCodes = true
},
confirmBackupCodes () { // confirm getting new backup codes
this.fetchBackupCodes().then((res) => {
this.backupCodes.getNewCodes = false
})
},
cancelBackupCodes () { // cancel confirm form of new backup codes
this.backupCodes.getNewCodes = false
},
// Setup OTP
setupOTP () { // prepare setup OTP
this.setupState.state = 'setupOTP'
this.setupState.setupOTPState = 'prepare'
this.backendInteractor.mfaSetupOTP()
.then((res) => {
this.otpSettings = res
this.setupState.setupOTPState = 'confirm'
})
},
doConfirmOTP () { // handler confirm enable OTP
this.error = null
this.backendInteractor.mfaConfirmOTP({
token: this.otpConfirmToken,
password: this.currentPassword
})
.then((res) => {
if (res.error) {
this.error = res.error
return
}
this.completeSetup()
})
},
completeSetup () {
this.setupState.setupOTPState = 'complete'
this.setupState.state = 'complete'
this.currentPassword = null
this.error = null
this.fetchSettings()
},
cancelSetup () { // cancel setup
this.setupState.setupOTPState = ''
this.setupState.state = ''
this.currentPassword = null
this.error = null
},
// end Setup OTP
// fetch settings from server
async fetchSettings () {
let result = await this.backendInteractor.fetchSettingsMFA()
this.settings = result.settings
return result
}
},
mounted () {
this.fetchSettings().then(() => {
this.readyInit = true
})
}
}
export default Mfa

View file

@ -0,0 +1,121 @@
<template>
<div class="setting-item mfa-settings" v-if="readyInit">
<div class="mfa-heading">
<h2>{{$t('settings.mfa.title')}}</h2>
</div>
<div>
<div class="setting-item" v-if="!setupInProgress">
<!-- Enabled methods -->
<h3>{{$t('settings.mfa.authentication_methods')}}</h3>
<totp-item :settings="settings" @deactivate="fetchSettings" @activate="activateOTP"/>
<br />
<div v-if="settings.enabled"> <!-- backup codes block-->
<recovery-codes :backup-codes="backupCodes" v-if="!confirmNewBackupCodes" />
<button class="btn btn-default" @click="getBackupCodes" v-if="!confirmNewBackupCodes">
{{$t('settings.mfa.generate_new_recovery_codes')}}
</button>
<div v-if="confirmNewBackupCodes">
<confirm @confirm="confirmBackupCodes" @cancel="cancelBackupCodes"
:disabled="backupCodes.inProgress">
<p class="warning">{{$t('settings.mfa.warning_of_generate_new_codes')}}</p>
</confirm>
</div>
</div>
</div>
<div v-if="setupInProgress"> <!-- setup block-->
<h3>{{$t('settings.mfa.setup_otp')}}</h3>
<recovery-codes :backup-codes="backupCodes" v-if="!setupOTPInProgress"/>
<button class="btn btn-default" @click="cancelSetup" v-if="canSetupOTP">
{{$t('general.cancel')}}
</button>
<button class="btn btn-default" v-if="canSetupOTP" @click="setupOTP">
{{$t('settings.mfa.setup_otp')}}
</button>
<template v-if="setupOTPInProgress">
<i v-if="prepareOTP">{{$t('settings.mfa.wait_pre_setup_otp')}}</i>
<div v-if="confirmOTP">
<div class="setup-otp">
<div class="qr-code">
<h4>{{$t('settings.mfa.scan.title')}}</h4>
<p>{{$t('settings.mfa.scan.desc')}}</p>
<qrcode :value="otpSettings.provisioning_uri" :options="{ width: 200 }"></qrcode>
<p>
{{$t('settings.mfa.scan.secret_code')}}:
{{otpSettings.key}}
</p>
</div>
<div class="verify">
<h4>{{$t('general.verify')}}</h4>
<p>{{$t('settings.mfa.verify.desc')}}</p>
<input type="text" v-model="otpConfirmToken">
<p>{{$t('settings.enter_current_password_to_confirm')}}:</p>
<input type="password" v-model="currentPassword">
<div class="confirm-otp-actions">
<button class="btn btn-default" @click="doConfirmOTP">
{{$t('settings.mfa.confirm_and_enable')}}
</button>
<button class="btn btn-default" @click="cancelSetup">
{{$t('general.cancel')}}
</button>
</div>
<div class="alert error" v-if="error">{{error}}</div>
</div>
</div>
</div>
</template>
</div>
</div>
</div>
</template>
<script src="./mfa.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.warning {
color: $fallback--cOrange;
color: var(--cOrange, $fallback--cOrange);
}
.mfa-settings {
.mfa-heading, .method-item {
overflow: hidden;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: baseline;
}
.setup-otp {
display: flex;
justify-content: center;
flex-wrap: wrap;
.qr-code {
flex: 1;
padding-right: 10px;
}
.verify { flex: 1; }
.error { margin: 4px 0 0 0; }
.confirm-otp-actions {
button {
width: 15em;
margin-top: 5px;
}
}
}
}
</style>

View file

@ -0,0 +1,17 @@
export default {
props: {
backupCodes: {
type: Object,
default: () => ({
inProgress: false,
codes: []
})
}
},
data: () => ({}),
computed: {
inProgress () { return this.backupCodes.inProgress },
ready () { return this.backupCodes.codes.length > 0 },
displayTitle () { return this.inProgress || this.ready }
}
}

View file

@ -0,0 +1,22 @@
<template>
<div>
<h4 v-if="displayTitle">{{$t('settings.mfa.recovery_codes')}}</h4>
<i v-if="inProgress">{{$t('settings.mfa.waiting_a_recovery_codes')}}</i>
<template v-if="ready">
<p class="alert warning">{{$t('settings.mfa.recovery_codes_warning')}}</p>
<ul class="backup-codes"><li v-for="code in backupCodes.codes">{{code}}</li></ul>
</template>
</div>
</template>
<script src="./mfa_backup_codes.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.warning {
color: $fallback--cOrange;
color: var(--cOrange, $fallback--cOrange);
}
.backup-codes {
font-family: var(--postCodeFont, monospace);
}
</style>

View file

@ -0,0 +1,49 @@
import Confirm from './confirm.vue'
import { mapState } from 'vuex'
export default {
props: ['settings'],
data: () => ({
error: false,
currentPassword: '',
deactivate: false,
inProgress: false // progress peform request to disable otp method
}),
components: {
'confirm': Confirm
},
computed: {
isActivated () {
return this.settings.totp
},
...mapState({
backendInteractor: (state) => state.api.backendInteractor
})
},
methods: {
doActivate () {
this.$emit('activate')
},
cancelDeactivate () { this.deactivate = false },
doDeactivate () {
this.error = null
this.deactivate = true
},
confirmDeactivate () { // confirm deactivate TOTP method
this.error = null
this.inProgress = true
this.backendInteractor.mfaDisableOTP({
password: this.currentPassword
})
.then((res) => {
this.inProgress = false
if (res.error) {
this.error = res.error
return
}
this.deactivate = false
this.$emit('deactivate')
})
}
}
}

View file

@ -0,0 +1,23 @@
<template>
<div>
<div class="method-item">
<strong>{{$t('settings.mfa.otp')}}</strong>
<button class="btn btn-default" v-if="!isActivated" @click="doActivate">
{{$t('general.enable')}}
</button>
<button class="btn btn-default" :disabled="deactivate" @click="doDeactivate"
v-if="isActivated">
{{$t('general.disable')}}
</button>
</div>
<confirm @confirm="confirmDeactivate" @cancel="cancelDeactivate"
:disabled="inProgress" v-if="deactivate">
{{$t('settings.enter_current_password_to_confirm')}}:
<input type="password" v-model="currentPassword">
</confirm>
<div class="alert error" v-if="error">{{error}}</div>
</div>
</template>
<script src="./mfa_totp.js"></script>

View file

@ -17,6 +17,7 @@ import Importer from '../importer/importer.vue'
import Exporter from '../exporter/exporter.vue'
import withSubscription from '../../hocs/with_subscription/with_subscription'
import userSearchApi from '../../services/new_api/user_search.js'
import Mfa from './mfa.vue'
const BlockList = withSubscription({
fetch: (props, $store) => $store.dispatch('fetchBlocks'),
@ -75,7 +76,8 @@ const UserSettings = {
MuteCard,
ProgressButton,
Importer,
Exporter
Exporter,
Mfa
},
computed: {
user () {

View file

@ -152,7 +152,7 @@
</tbody>
</table>
</div>
<mfa />
<div class="setting-item">
<h2>{{$t('settings.delete_account')}}</h2>
<p v-if="!deletingAccount">{{$t('settings.delete_account_description')}}</p>