Ponkhy
3 years ago
11 changed files with 447 additions and 15 deletions
@ -0,0 +1,10 @@ |
|||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. |
|||
BEGIN TRANSACTION; |
|||
|
|||
ALTER TABLE user |
|||
ADD twofa_secret VARCHAR(64); |
|||
|
|||
ALTER TABLE user |
|||
ADD twofa_status BOOLEAN default 0; |
|||
|
|||
COMMIT; |
@ -0,0 +1,178 @@ |
|||
<template> |
|||
<form @submit.prevent="submit"> |
|||
<div ref="modal" class="modal fade" tabindex="-1" data-bs-backdrop="static"> |
|||
<div class="modal-dialog"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header"> |
|||
<h5 class="modal-title"> |
|||
{{ $t("Setup 2FA") }} |
|||
<span v-if="twoFAStatus == true" class="badge bg-primary">{{ $t("Active") }}</span> |
|||
<span v-if="twoFAStatus == false" class="badge bg-primary">{{ $t("Inactive") }}</span> |
|||
</h5> |
|||
<button :disabled="processing" type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" /> |
|||
</div> |
|||
<div class="modal-body"> |
|||
<div class="mb-3"> |
|||
<div v-if="uri && twoFAStatus == false" class="mx-auto text-center" style="width: 210px;"> |
|||
<vue-qrcode :key="uri" :value="uri" type="image/png" :quality="1" :color="{ light: '#ffffffff' }" /> |
|||
<button v-show="!showURI" type="button" class="btn btn-outline-primary btn-sm mt-2" @click="showURI = true">Show URI</button> |
|||
</div> |
|||
<p v-if="showURI && twoFAStatus == false" class="text-break mt-2">{{ uri }}</p> |
|||
|
|||
<button v-if="uri == null && twoFAStatus == false" class="btn btn-primary" type="button" @click="prepare2FA()"> |
|||
{{ $t("Enable 2FA") }} |
|||
</button> |
|||
|
|||
<button v-if="twoFAStatus == true" class="btn btn-danger" type="button" :disabled="processing" @click="confirmDisableTwoFA()"> |
|||
{{ $t("Disable 2FA") }} |
|||
</button> |
|||
|
|||
<div v-if="uri && twoFAStatus == false" class="mt-3"> |
|||
<label for="basic-url" class="form-label">{{ $t("twoFAVerifyLabel") }}</label> |
|||
<div class="input-group"> |
|||
<input v-model="token" type="text" class="form-control"> |
|||
<button class="btn btn-outline-primary" type="button" @click="verifyToken()">{{ $t("Verify Token") }}</button> |
|||
</div> |
|||
<p v-show="tokenValid" class="mt-2" style="color: green">{{ $t("tokenValidSettingsMsg") }}</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div v-if="twoFAStatus == false" class="modal-footer"> |
|||
<button type="submit" class="btn btn-primary" :disabled="processing || tokenValid == false" @click="confirmEnableTwoFA()"> |
|||
<div v-if="processing" class="spinner-border spinner-border-sm me-1"></div> |
|||
{{ $t("Save") }} |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
|
|||
<Confirm ref="confirmEnableTwoFA" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="save2FA"> |
|||
{{ $t("confirmEnableTwoFAMsg") }} |
|||
</Confirm> |
|||
|
|||
<Confirm ref="confirmDisableTwoFA" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="disable2FA"> |
|||
{{ $t("confirmDisableTwoFAMsg") }} |
|||
</Confirm> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import { Modal } from "bootstrap" |
|||
import Confirm from "./Confirm.vue"; |
|||
import VueQrcode from "vue-qrcode" |
|||
import { useToast } from "vue-toastification" |
|||
const toast = useToast() |
|||
|
|||
export default { |
|||
components: { |
|||
Confirm, |
|||
VueQrcode, |
|||
}, |
|||
props: {}, |
|||
data() { |
|||
return { |
|||
processing: false, |
|||
uri: null, |
|||
tokenValid: false, |
|||
twoFAStatus: null, |
|||
token: null, |
|||
showURI: false, |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.modal = new Modal(this.$refs.modal) |
|||
this.getStatus(); |
|||
}, |
|||
methods: { |
|||
show() { |
|||
this.modal.show() |
|||
}, |
|||
|
|||
confirmEnableTwoFA() { |
|||
this.$refs.confirmEnableTwoFA.show() |
|||
}, |
|||
|
|||
confirmDisableTwoFA() { |
|||
this.$refs.confirmDisableTwoFA.show() |
|||
}, |
|||
|
|||
prepare2FA() { |
|||
this.processing = true; |
|||
|
|||
this.$root.getSocket().emit("prepare2FA", (res) => { |
|||
this.processing = false; |
|||
|
|||
if (res.ok) { |
|||
this.uri = res.uri; |
|||
} else { |
|||
toast.error(res.msg); |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
save2FA() { |
|||
this.processing = true; |
|||
|
|||
this.$root.getSocket().emit("save2FA", (res) => { |
|||
this.processing = false; |
|||
|
|||
if (res.ok) { |
|||
this.$root.toastRes(res) |
|||
this.getStatus(); |
|||
this.modal.hide(); |
|||
} else { |
|||
toast.error(res.msg); |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
disable2FA() { |
|||
this.processing = true; |
|||
|
|||
this.$root.getSocket().emit("disable2FA", (res) => { |
|||
this.processing = false; |
|||
|
|||
if (res.ok) { |
|||
this.$root.toastRes(res) |
|||
this.getStatus(); |
|||
this.modal.hide(); |
|||
} else { |
|||
toast.error(res.msg); |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
verifyToken() { |
|||
this.$root.getSocket().emit("verifyToken", this.token, (res) => { |
|||
if (res.ok) { |
|||
this.tokenValid = res.valid; |
|||
} else { |
|||
toast.error(res.msg); |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
getStatus() { |
|||
this.$root.getSocket().emit("twoFAStatus", (res) => { |
|||
if (res.ok) { |
|||
this.twoFAStatus = res.status; |
|||
} else { |
|||
toast.error(res.msg); |
|||
} |
|||
}) |
|||
}, |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
@import "../assets/vars.scss"; |
|||
|
|||
.dark { |
|||
.modal-dialog .form-text, .modal-dialog p { |
|||
color: $dark-font-color; |
|||
} |
|||
} |
|||
</style> |
Loading…
Reference in new issue