26 changed files with 518 additions and 78 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 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