Revert "add TOTP/Recovery Form for mobile version"
This reverts commit a3811f944819430c278b6da6b08dc322a9b9ff65.
This commit is contained in:
parent
9df99c5205
commit
77eceedbf7
32 changed files with 1657 additions and 439 deletions
9
src/components/user_settings/confirm.js
Normal file
9
src/components/user_settings/confirm.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
const Confirm = {
|
||||
props: ['disabled'],
|
||||
data: () => ({}),
|
||||
methods: {
|
||||
confirm () { this.$emit('confirm') },
|
||||
cancel () { this.$emit('cancel') }
|
||||
}
|
||||
}
|
||||
export default Confirm
|
14
src/components/user_settings/confirm.vue
Normal file
14
src/components/user_settings/confirm.vue
Normal 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>
|
152
src/components/user_settings/mfa.js
Normal file
152
src/components/user_settings/mfa.js
Normal 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
|
121
src/components/user_settings/mfa.vue
Normal file
121
src/components/user_settings/mfa.vue
Normal 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>
|
17
src/components/user_settings/mfa_backup_codes.js
Normal file
17
src/components/user_settings/mfa_backup_codes.js
Normal 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 }
|
||||
}
|
||||
}
|
22
src/components/user_settings/mfa_backup_codes.vue
Normal file
22
src/components/user_settings/mfa_backup_codes.vue
Normal 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>
|
49
src/components/user_settings/mfa_totp.js
Normal file
49
src/components/user_settings/mfa_totp.js
Normal 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')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
23
src/components/user_settings/mfa_totp.vue
Normal file
23
src/components/user_settings/mfa_totp.vue
Normal 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>
|
|
@ -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 () {
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue