commit
02247c4174
@ -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 NOT NULL;
|
||||||
|
|
||||||
|
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">{{ $t("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" maxlength="6" 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="uri && 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