2FAuth/public/build/assets/Import-CWswmNcW.js.map
2024-11-27 12:03:02 +01:00

1 line
31 KiB
Plaintext

{"version":3,"file":"Import-CWswmNcW.js","sources":["../../../resources/js/components/formElements/FormTextarea.vue","../../../resources/js/views/twofaccounts/Import.vue"],"sourcesContent":["<script setup>\n import { useIdGenerator, useValidationErrorIdGenerator } from '@/composables/helpers'\n\n defineOptions({\n inheritAttrs: false\n })\n\n const props = defineProps({\n modelValue: [String, Number, Boolean],\n label: {\n type: String,\n default: ''\n },\n fieldName: {\n type: String,\n default: '',\n required: true\n },\n fieldError: [String],\n placeholder: {\n type: String,\n default: ''\n },\n help: {\n type: String,\n default: ''\n },\n size: {\n type: String,\n default: ''\n },\n hasOffset: {\n type: Boolean,\n default: false\n },\n isDisabled: {\n type: Boolean,\n default: false\n },\n maxLength: {\n type: Number,\n default: null\n },\n isIndented: Boolean,\n leftIcon: '',\n rightIcon: '',\n idSuffix: {\n type: String,\n default: ''\n }\n })\n\n const { inputId } = useIdGenerator(props.inputType, props.fieldName + props.idSuffix)\n const { valErrorId } = useValidationErrorIdGenerator(props.fieldName)\n const legendId = useIdGenerator('legend', props.fieldName).inputId\n</script>\n\n<template>\n <div class=\"mb-3\" :class=\"{ 'pt-3' : hasOffset, 'is-flex' : isIndented }\">\n <div v-if=\"isIndented\" class=\"mx-2 pr-1\" :style=\"{ 'opacity': isDisabled ? '0.5' : '1' }\">\n <FontAwesomeIcon class=\"has-text-grey\" :icon=\"['fas', 'chevron-right']\" transform=\"rotate-135\"/>\n </div>\n <div class=\"field\" :class=\"{ 'is-flex-grow-5' : isIndented }\">\n <label v-if=\"label\" :for=\"inputId\" class=\"label\" v-html=\"$t(label)\"></label>\n <div class=\"control\" :class=\"{ 'has-icons-left' : leftIcon, 'has-icons-right': rightIcon }\">\n <textarea \n :disabled=\"isDisabled\" \n :id=\"inputId\"\n class=\"textarea\" \n :class=\"size\"\n :value=\"modelValue\" \n :placeholder=\"placeholder\" \n v-bind=\"$attrs\"\n v-on:input=\"$emit('update:modelValue', $event.target.value)\"\n v-on:change=\"$emit('change:modelValue', $event.target.value)\"\n :maxlength=\"maxLength\"\n :aria-describedby=\"help ? legendId : undefined\"\n :aria-invalid=\"fieldError != undefined\"\n :aria-errormessage=\"fieldError != undefined ? valErrorId : undefined\" \n />\n </div>\n <FieldError v-if=\"fieldError != undefined\" :error=\"fieldError\" :field=\"fieldName\" />\n <p :id=\"legendId\" class=\"help\" v-html=\"$t(help)\" v-if=\"help\"></p>\n </div>\n </div> \n</template>\n","<script setup>\n import Form from '@/components/formElements/Form'\n import FormTextarea from '@/components/formElements/FormTextarea.vue'\n import twofaccountService from '@/services/twofaccountService'\n import OtpDisplay from '@/components/OtpDisplay.vue'\n import Spinner from '@/components/Spinner.vue'\n import { useNotifyStore } from '@/stores/notify'\n import { useUserStore } from '@/stores/user'\n import { useBusStore } from '@/stores/bus'\n import { useTwofaccounts } from '@/stores/twofaccounts'\n import { UseColorMode } from '@vueuse/components'\n\n const $2fauth = inject('2fauth')\n const notify = useNotifyStore()\n const user = useUserStore()\n const bus = useBusStore()\n const twofaccounts = useTwofaccounts()\n const otpDisplay = ref(null)\n const fileInput = ref(null)\n const qrcodeInput = ref(null)\n const directInput = ref(null)\n const directInputError = ref(null)\n const form = reactive(new Form({\n service: '',\n account: '',\n otp_type: '',\n icon: '',\n secret: '',\n algorithm: '',\n digits: null,\n counter: null,\n period: null,\n }))\n const fileForm = reactive(new Form({\n file: null,\n withSecret: true\n }))\n const qrcodeForm = reactive(new Form({\n qrcode: null,\n withSecret: true\n }))\n const showTwofaccountInModal = ref(false)\n const supportedSources = [\n {app: '2FAuth', format: 'JSON'},\n {app: 'Google Auth', format: trans('twofaccounts.import.qr_code')},\n {app: 'Aegis Auth', format: 'JSON'},\n {app: 'Aegis Auth', format: trans('twofaccounts.import.plain_text')},\n {app: '2FAS auth', format: 'JSON'},\n ]\n const exportedAccounts = ref([])\n const isFetching = ref(false)\n\n const importableCount = computed(() => {\n return exportedAccounts.value.filter(account => account.imported == -1 && account.id > -2).length\n })\n\n const duplicateCount = computed(() => {\n return exportedAccounts.value.filter(account => account.id === -1 && account.imported === -1).length\n })\n\n const importedCount = computed(() => {\n return exportedAccounts.value.filter(account => account.imported === 1).length\n })\n\n watch(showTwofaccountInModal, (val) => {\n if (val == false) {\n otpDisplay.value?.clearOTP()\n }\n })\n\n onMounted(() => {\n // A migration URI has been provided by the Start view using the bus store\n // We extract the accounts from the URI and list them in the view\n if( bus.migrationUri ) {\n migrate(bus.migrationUri)\n bus.migrationUri = null\n }\n })\n\n /**\n * Posts the migration payload\n */\n async function migrate(payload) {\n isFetching.value = true\n\n await twofaccountService.migrate(payload, { returnError: true }).then(response => {\n response.data.forEach((data) => {\n data.imported = -1;\n exportedAccounts.value.push(data)\n })\n notifyValidAccountFound()\n directInput.value = directInputError.value = null\n })\n .catch(error => {\n notify.alert({ text: trans(error.response.data.message) })\n });\n\n isFetching.value = false\n }\n\n /**\n * Removes all duplicates from the accounts list\n */\n function discardDuplicates() {\n if(confirm(trans('twofaccounts.confirm.discard_duplicates'))) {\n notify.clear()\n otpDisplay.value?.clearOTP()\n exportedAccounts.value = exportedAccounts.value.filter(account => account.id !== -1)\n }\n }\n\n /**\n * Clears the accounts list\n */\n function discardAccounts() {\n if(confirm(trans('twofaccounts.confirm.discard_all'))) {\n notify.clear()\n otpDisplay.value?.clearOTP()\n exportedAccounts.value = []\n }\n }\n\n /**\n * Removes one duplicate from the accounts list\n */\n function discardAccount(accountIndex) {\n if(confirm(trans('twofaccounts.confirm.discard'))) {\n exportedAccounts.value.splice(accountIndex, 1)\n }\n }\n\n /**\n * Batch stores valid accounts, even duplicates\n */\n async function createAccounts() {\n for (let index = 0; index < exportedAccounts.value.length; index++) {\n if (exportedAccounts.value[index].imported == -1) {\n await createAccount(index)\n }\n }\n }\n\n /**\n * Stores the provided account\n */\n async function createAccount(accountIndex) {\n form.fill(exportedAccounts.value[accountIndex])\n\n await form.post('/api/v1/twofaccounts', {returnError: true})\n .then(response => {\n exportedAccounts.value[accountIndex].imported = 1\n exportedAccounts.value[accountIndex].id = response.data.id\n delete response.data.secret\n twofaccounts.items.push(response.data)\n })\n .catch(error => {\n exportedAccounts.value[accountIndex].imported = 0\n exportedAccounts.value[accountIndex].id = 0\n exportedAccounts.value[accountIndex].errors = form.errors.flatten()\n })\n }\n\n /**\n * Generates a fresh OTP password and displays it\n */\n function previewAccount(accountIndex) {\n form.fill(exportedAccounts.value[accountIndex])\n showTwofaccountInModal.value = true\n\n nextTick().then(() => {\n otpDisplay.value.show()\n })\n }\n\n /**\n * Uploads the submitted file to the backend for parsing\n */\n function submitFile() {\n fileForm.clear()\n isFetching.value = true\n fileForm.file = fileInput.value.files[0]\n\n fileForm.upload('/api/v1/twofaccounts/migration', { returnError: true }).then(response => {\n response.data.forEach((data) => {\n data.imported = -1;\n exportedAccounts.value.push(data)\n })\n notifyValidAccountFound()\n })\n .catch(error => {\n if (error.response.status === 422) {\n if (error.response.data.errors.file == undefined) {\n notify.alert({ text: trans('errors.invalid_2fa_data') })\n }\n }\n else notify.alert({ text: error.response.data.message})\n })\n \n isFetching.value = false\n }\n\n /**\n * Uploads the submitted QR code file to the backend for decoding\n */\n function submitQrCode() {\n qrcodeForm.clear()\n isFetching.value = true\n qrcodeForm.qrcode = qrcodeInput.value.files[0]\n\n qrcodeForm.upload('/api/v1/qrcode/decode', { returnError: true }).then(response => {\n migrate(response.data.data)\n })\n .catch(error => {\n if( error.response.status === 422 ) {\n if (error.response.data.errors.qrcode == undefined) {\n notify.alert({ text: trans('errors.invalid_2fa_data') })\n }\n }\n else notify.alert({ text: error.response.data.message})\n })\n \n isFetching.value = false\n }\n\n /**\n * Notifies that valid account(s) have been found for import\n */\n function notifyValidAccountFound() {\n notify.success({ text: trans('twofaccounts.import.x_valid_accounts_found', { count: importableCount.value }) })\n }\n\n /**\n * Submits the directInput form to the backend\n */\n function submitDirectInput() {\n directInputError.value = null\n \n if (! directInput.value) {\n directInputError.value = trans('validation.required', { attribute: 'Direct input' })\n }\n else migrate(directInput.value)\n }\n\n</script>\n\n<template>\n <UseColorMode v-slot=\"{ mode }\">\n <div>\n <ResponsiveWidthWrapper>\n <h1 class=\"title has-text-grey-dark\">\n {{ $t('twofaccounts.import.import') }}\n </h1>\n <div v-if=\"!isFetching && exportedAccounts.length == 0\">\n <div class=\"block is-size-7-mobile\">\n <p class=\"mb-2\">{{ $t('twofaccounts.import.import_legend') }}</p>\n <p>{{ $t('twofaccounts.import.import_legend_afterpart') }}</p>\n </div>\n <div class=\"columns\">\n <div class=\"column\">\n <div class=\"block\">\n <div class=\"card\">\n <div class=\"card-content\">\n <div class=\"media\">\n <div class=\"media-left\">\n <figure class=\"image is-32x32\">\n <FontAwesomeIcon :icon=\"['fas', 'qrcode']\" size=\"2x\" :class=\"mode == 'dark' ? 'has-text-grey-darker' : 'has-text-grey-lighter'\" />\n </figure>\n </div>\n <div class=\"media-content\">\n <p class=\"title is-5 has-text-grey\" v-html=\"$t('twofaccounts.import.qr_code')\" />\n <p class=\"subtitle is-6 is-size-7-mobile\">{{ $t('twofaccounts.import.supported_formats_for_qrcode_upload') }}</p>\n </div>\n </div>\n <FieldError v-if=\"qrcodeForm.errors.hasAny('qrcode')\" :error=\"qrcodeForm.errors.get('qrcode')\" :field=\"'qrcode'\" />\n </div>\n <footer class=\"card-footer\">\n <RouterLink id=\"btnCapture\" :to=\"{ name: 'capture' }\" class=\"card-footer-item\">\n {{ $t('twofaccounts.import.scan') }}\n </RouterLink>\n <a role=\"button\" tabindex=\"0\" class=\"card-footer-item is-relative\" @click=\"qrcodeInput.click()\" @keyup.enter=\"qrcodeInput.click()\">\n <input inert tabindex=\"-1\" class=\"file-input\" type=\"file\" accept=\"image/*\" v-on:change=\"submitQrCode\" ref=\"qrcodeInput\">\n {{ $t('twofaccounts.import.upload') }}\n </a>\n </footer>\n </div>\n </div>\n <div class=\"block\">\n <div class=\"card\">\n <div class=\"card-content\">\n <div class=\"media\">\n <div class=\"media-left\">\n <figure class=\"image is-32x32\">\n <FontAwesomeIcon :icon=\"['fas', 'file-lines']\" size=\"2x\" :class=\"mode == 'dark' ? 'has-text-grey-darker' : 'has-text-grey-lighter'\" />\n </figure>\n </div>\n <div class=\"media-content\">\n <p class=\"title is-5 has-text-grey\">{{ $t('twofaccounts.import.text_file') }}</p>\n <p class=\"subtitle is-6 is-size-7-mobile\">{{ $t('twofaccounts.import.supported_formats_for_file_upload') }}</p>\n </div>\n </div>\n <FieldError v-if=\"fileForm.errors.hasAny('file')\" :error=\"fileForm.errors.get('file')\" :field=\"'file'\" />\n </div>\n <footer class=\"card-footer\">\n <a role=\"button\" tabindex=\"0\" class=\"card-footer-item is-relative\" @click=\"fileInput.click()\" @keyup.enter=\"fileInput.click()\">\n <input inert tabindex=\"-1\" class=\"file-input\" type=\"file\" accept=\"text/plain,application/json,text/csv,.2fas\" v-on:change=\"submitFile\" ref=\"fileInput\">\n {{ $t('twofaccounts.import.upload') }}\n </a>\n </footer>\n </div>\n </div>\n <div class=\"block\">\n <div class=\"card\">\n <div class=\"card-content\">\n <div class=\"media\">\n <div class=\"media-left\">\n <figure class=\"image is-32x32\">\n <FontAwesomeIcon :icon=\"['fas', 'align-left']\" size=\"2x\" :class=\"mode == 'dark' ? 'has-text-grey-darker' : 'has-text-grey-lighter'\" />\n </figure>\n </div>\n <div class=\"media-content\">\n <p class=\"title is-5 has-text-grey\" v-html=\"$t('twofaccounts.import.direct_input')\" />\n <p class=\"subtitle is-6 is-size-7-mobile\">{{ $t('twofaccounts.import.expected_format_for_direct_input') }}</p>\n </div>\n </div>\n <div class=\"content\">\n <FormTextarea v-model=\"directInput\" :fieldError=\"directInputError\" fieldName=\"payload\" rows=\"5\" :size=\"'is-small'\" />\n </div>\n </div>\n <footer class=\"card-footer\">\n <a role=\"button\" tabindex=\"0\" class=\"card-footer-item is-relative\" @click.stop=\"submitDirectInput\">\n {{ $t('commons.submit') }}\n </a>\n </footer>\n </div>\n </div>\n </div>\n </div>\n <!-- Supported migration resources -->\n <h2 class=\"title is-5 has-text-grey-dark\">{{ $t('twofaccounts.import.supported_migration_formats') }}</h2>\n <div class=\"block is-size-7-mobile\">\n <FontAwesomeIcon :icon=\"['fas', 'fa-triangle-exclamation']\" class=\"has-text-warning-dark\" />\n {{ $t('twofaccounts.import.do_not_set_password_or_encryption') }}\n </div>\n <table class=\"table is-size-7-mobile is-fullwidth\">\n <thead>\n <tr>\n <th></th>\n <th>Plain text</th>\n <th>QR code</th>\n <th>JSON</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>Google Authenticator</th>\n <td></td>\n <td><FontAwesomeIcon :icon=\"['fas', 'circle-check']\" /></td>\n <td></td>\n </tr>\n <tr>\n <th>Aegis Auth</th>\n <td><FontAwesomeIcon :icon=\"['fas', 'circle-check']\" /></td>\n <td></td>\n <td><FontAwesomeIcon :icon=\"['fas', 'circle-check']\" /></td>\n </tr>\n <tr>\n <th>2FAS auth</th>\n <td></td>\n <td></td>\n <td><FontAwesomeIcon :icon=\"['fas', 'circle-check']\" /></td>\n </tr>\n <tr>\n <th>FreeOTP+</th>\n <td><FontAwesomeIcon :icon=\"['fas', 'circle-check']\" /></td>\n <td></td>\n <td></td>\n </tr>\n <tr>\n <th>2FAuth</th>\n <td></td>\n <td></td>\n <td><FontAwesomeIcon :icon=\"['fas', 'circle-check']\" /></td>\n </tr>\n </tbody>\n </table>\n </div>\n <div v-else-if=\"isFetching && exportedAccounts.length === 0\">\n <Spinner :type=\"'fullscreen-overlay'\" :isVisible=\"true\" :message=\"'twofaccounts.import.parsing_data'\" />\n </div>\n <div v-else>\n <div class=\"block is-size-7-mobile\">\n <p class=\"mb-2\">{{ $t('twofaccounts.import.submitted_data_parsed_now_accounts_are_awaiting_import') }}</p>\n <p>{{ $t('twofaccounts.import.use_buttons_to_save_or_discard') }}</p>\n </div>\n <div v-for=\"(account, index) in exportedAccounts\" :key=\"account.name\" class=\"group-item is-size-5 is-size-6-mobile\">\n <div class=\"is-flex is-justify-content-space-between\">\n <!-- Account name -->\n <div v-if=\"account.id > -2 && account.imported !== 0\" class=\"is-flex-grow-1 has-ellipsis is-clickable\" @click=\"previewAccount(index)\" :title=\"$t('twofaccounts.import.generate_a_test_password')\">\n <img role=\"presentation\" v-if=\"account.icon && user.preferences.showAccountsIcons\" class=\"import-icon\" :src=\"$2fauth.config.subdirectory + '/storage/icons/' + account.icon\" alt=\"\">\n {{ account.account }}\n </div>\n <div v-else class=\"is-flex-grow-1 has-ellipsis\">{{ account.account }}</div>\n <!-- buttons -->\n <div v-if=\"account.imported === -1\" class=\"tags is-flex-wrap-nowrap\">\n <!-- discard button -->\n <button type=\"button\" class=\"button tag\" :class=\"{'is-dark has-text-grey-light' : mode == 'dark'}\" @click=\"discardAccount(index)\" :title=\"$t('twofaccounts.import.discard_this_account')\">\n <FontAwesomeIcon :icon=\"['fas', 'trash']\" />\n </button>\n <!-- import button -->\n <button v-if=\"account.id > -2\" type=\"button\" class=\"button tag is-link\" @click=\"createAccount(index)\" :title=\"$t('twofaccounts.import.import_this_account')\">\n {{ $t('twofaccounts.import.to_import') }}\n </button>\n </div>\n <!-- result label -->\n <div v-else class=\"has-nowrap\">\n <span v-if=\"account.imported === 1\" class=\"has-text-success\">\n {{ $t('twofaccounts.import.imported') }} <FontAwesomeIcon :icon=\"['fas', 'check']\" />\n </span>\n <span v-else class=\"has-text-danger\">\n {{ $t('twofaccounts.import.failure') }} <FontAwesomeIcon :icon=\"['fas', 'times']\" />\n </span>\n </div>\n </div>\n <div class=\"is-size-6 is-size-7-mobile\">\n <!-- service name -->\n <div class=\"is-family-primary has-text-grey\">{{ $t('twofaccounts.import.issuer') }}: {{ account.service }}</div>\n <!-- reasons to invalid G-Auth data -->\n <div v-if=\"account.id === -2\" class=\"has-text-danger\">\n <FontAwesomeIcon class=\"mr-1\" :icon=\"['fas', 'times-circle']\" />{{ account.secret }}\n </div>\n <!-- possible duplicates -->\n <div v-if=\"account.id === -1 && account.imported !== 1 && !account.errors\" class=\"has-text-warning\">\n <FontAwesomeIcon class=\"mr-1\" :icon=\"['fas', 'exclamation-circle']\" />{{ $t('twofaccounts.import.possible_duplicate') }}\n </div>\n <!-- errors during account creation -->\n <ul v-if=\"account.errors\">\n <li v-for=\"(error) in account.errors\" :key=\"error\" class=\"has-text-danger\">{{ error }}</li>\n </ul>\n </div>\n </div>\n <!-- discard links -->\n <div v-if=\"importableCount > 0\" class=\"mt-2 is-size-7 is-pulled-right\">\n <button v-if=\"duplicateCount\" @click=\"discardDuplicates()\" type=\"button\" class=\"has-text-grey button is-small is-ghost\">{{ $t('twofaccounts.import.discard_duplicates') }} ({{duplicateCount}})</button>\n <button @click=\"discardAccounts()\" type=\"button\" class=\"has-text-grey button is-small is-ghost\">{{ $t('twofaccounts.import.discard_all') }}</button>\n </div>\n <div v-if=\"importedCount == exportedAccounts.length\" class=\"mt-2 is-size-7 is-pulled-right\">\n <button @click=\"exportedAccounts = []\" type=\"button\" class=\"has-text-grey button is-small is-ghost\">{{ $t('commons.clear') }}</button>\n </div>\n </div>\n <!-- footer -->\n <VueFooter :showButtons=\"true\">\n <!-- Import all button -->\n <p class=\"control\" v-if=\"importableCount > 0\">\n <button type=\"button\" class=\"button is-link is-rounded is-focus\" @click=\"createAccounts\">\n <span>{{ $t('twofaccounts.import.import_all') }} ({{ importableCount }})</span>\n <!-- <span class=\"icon is-small\">\n <FontAwesomeIcon :icon=\"['fas', 'qrcode']\" />\n </span> -->\n </button>\n </p>\n <ButtonBackCloseCancel :returnTo=\"{ name: 'accounts' }\" :action=\"importableCount > 0 ? 'cancel' : 'close'\" />\n </VueFooter>\n </ResponsiveWidthWrapper>\n <!-- modal -->\n <modal v-model=\"showTwofaccountInModal\">\n <OtpDisplay\n ref=\"otpDisplay\"\n v-bind=\"form.data()\"\n @increment-hotp=\"\"\n @validation-error=\"\"\n @please-close-me=\"showTwofaccountInModal = false\">\n </OtpDisplay>\n </modal>\n </div>\n </UseColorMode>\n</template>\n"],"names":["props","__props","inputId","useIdGenerator","valErrorId","useValidationErrorIdGenerator","legendId","$2fauth","inject","notify","useNotifyStore","user","useUserStore","bus","useBusStore","twofaccounts","useTwofaccounts","otpDisplay","ref","fileInput","qrcodeInput","directInput","directInputError","form","reactive","Form","fileForm","qrcodeForm","showTwofaccountInModal","trans","exportedAccounts","isFetching","importableCount","computed","account","duplicateCount","importedCount","watch","val","_a","onMounted","migrate","payload","twofaccountService","response","data","notifyValidAccountFound","error","discardDuplicates","discardAccounts","discardAccount","accountIndex","createAccounts","index","createAccount","previewAccount","nextTick","submitFile","submitQrCode","submitDirectInput"],"mappings":"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"}