aboutsummaryrefslogtreecommitdiffstats
path: root/apps/twofactor_backupcodes/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/twofactor_backupcodes/src')
-rw-r--r--apps/twofactor_backupcodes/src/service/BackupCodesService.js16
-rw-r--r--apps/twofactor_backupcodes/src/service/PrintService.js16
-rw-r--r--apps/twofactor_backupcodes/src/settings.js20
-rw-r--r--apps/twofactor_backupcodes/src/store.js53
-rw-r--r--apps/twofactor_backupcodes/src/views/PersonalSettings.vue157
5 files changed, 262 insertions, 0 deletions
diff --git a/apps/twofactor_backupcodes/src/service/BackupCodesService.js b/apps/twofactor_backupcodes/src/service/BackupCodesService.js
new file mode 100644
index 00000000000..0abf365ccdb
--- /dev/null
+++ b/apps/twofactor_backupcodes/src/service/BackupCodesService.js
@@ -0,0 +1,16 @@
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import Axios from '@nextcloud/axios'
+import { generateUrl } from '@nextcloud/router'
+
+/**
+ *
+ */
+export function generateCodes() {
+ const url = generateUrl('/apps/twofactor_backupcodes/settings/create')
+
+ return Axios.post(url, {}).then(resp => resp.data)
+}
diff --git a/apps/twofactor_backupcodes/src/service/PrintService.js b/apps/twofactor_backupcodes/src/service/PrintService.js
new file mode 100644
index 00000000000..4575628744a
--- /dev/null
+++ b/apps/twofactor_backupcodes/src/service/PrintService.js
@@ -0,0 +1,16 @@
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+/**
+ * @param {any} data -
+ */
+export function print(data) {
+ const name = OC.theme.name || 'Nextcloud'
+ const newTab = window.open('', t('twofactor_backupcodes', '{name} backup codes', { name }))
+ newTab.document.write('<h1>' + t('twofactor_backupcodes', '{name} backup codes', { name }) + '</h1>')
+ newTab.document.write('<pre>' + data + '</pre>')
+ newTab.print()
+ newTab.close()
+}
diff --git a/apps/twofactor_backupcodes/src/settings.js b/apps/twofactor_backupcodes/src/settings.js
new file mode 100644
index 00000000000..53aa7e365af
--- /dev/null
+++ b/apps/twofactor_backupcodes/src/settings.js
@@ -0,0 +1,20 @@
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { loadState } from '@nextcloud/initial-state'
+import Vue from 'vue'
+
+import PersonalSettings from './views/PersonalSettings.vue'
+import store from './store.js'
+
+Vue.prototype.t = t
+
+const initialState = loadState('twofactor_backupcodes', 'state')
+store.replaceState(initialState)
+
+const View = Vue.extend(PersonalSettings)
+new View({
+ store,
+}).$mount('#twofactor-backupcodes-settings')
diff --git a/apps/twofactor_backupcodes/src/store.js b/apps/twofactor_backupcodes/src/store.js
new file mode 100644
index 00000000000..1d5cdcafc6d
--- /dev/null
+++ b/apps/twofactor_backupcodes/src/store.js
@@ -0,0 +1,53 @@
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import Vue from 'vue'
+import Vuex, { Store } from 'vuex'
+import { generateCodes } from './service/BackupCodesService.js'
+
+Vue.use(Vuex)
+
+const state = {
+ enabled: false,
+ total: 0,
+ used: 0,
+ codes: [],
+}
+
+const mutations = {
+ setEnabled(state, enabled) {
+ Vue.set(state, 'enabled', enabled)
+ },
+ setTotal(state, total) {
+ Vue.set(state, 'total', total)
+ },
+ setUsed(state, used) {
+ Vue.set(state, 'used', used)
+ },
+ setCodes(state, codes) {
+ Vue.set(state, 'codes', codes)
+ },
+}
+
+const actions = {
+ generate({ commit }) {
+ commit('setEnabled', false)
+
+ return generateCodes().then(({ codes, state }) => {
+ commit('setEnabled', state.enabled)
+ commit('setTotal', state.total)
+ commit('setUsed', state.used)
+ commit('setCodes', codes)
+ return true
+ })
+ },
+}
+
+export default new Store({
+ strict: process.env.NODE_ENV !== 'production',
+ state,
+ mutations,
+ actions,
+})
diff --git a/apps/twofactor_backupcodes/src/views/PersonalSettings.vue b/apps/twofactor_backupcodes/src/views/PersonalSettings.vue
new file mode 100644
index 00000000000..3e17613bb47
--- /dev/null
+++ b/apps/twofactor_backupcodes/src/views/PersonalSettings.vue
@@ -0,0 +1,157 @@
+<!--
+ - SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <div class="backupcodes-settings">
+ <NcButton v-if="!enabled"
+ id="generate-backup-codes"
+ :disabled="generatingCodes"
+ @click="generateBackupCodes">
+ <template #icon>
+ <NcLoadingIcon v-if="generatingCodes" />
+ </template>
+ {{ t('twofactor_backupcodes', 'Generate backup codes') }}
+ </NcButton>
+ <template v-else>
+ <p class="backupcodes-settings__codes">
+ <template v-if="!haveCodes">
+ {{ t('twofactor_backupcodes', 'Backup codes have been generated. {used} of {total} codes have been used.', {used, total}) }}
+ </template>
+ <template v-else>
+ {{ t('twofactor_backupcodes', 'These are your backup codes. Please save and/or print them as you will not be able to read the codes again later.') }}
+ <ul>
+ <li v-for="code in codes"
+ :key="code"
+ class="backupcodes-settings__codes__code">
+ {{ code }}
+ </li>
+ </ul>
+ </template>
+ </p>
+ <p class="backupcodes-settings__actions">
+ <template v-if="haveCodes">
+ <NcButton :href="downloadUrl"
+ :download="downloadFilename"
+ type="primary">
+ {{ t('twofactor_backupcodes', 'Save backup codes') }}
+ </NcButton>
+ <NcButton @click="printCodes">
+ {{ t('twofactor_backupcodes', 'Print backup codes') }}
+ </NcButton>
+ </template>
+ <NcButton id="generate-backup-codes"
+ @click="generateBackupCodes">
+ {{ t('twofactor_backupcodes', 'Regenerate backup codes') }}
+ </NcButton>
+ </p>
+ <p>
+ <em>
+ {{ t('twofactor_backupcodes', 'If you regenerate backup codes, you automatically invalidate old codes.') }}
+ </em>
+ </p>
+ </template>
+ </div>
+</template>
+
+<script>
+import { confirmPassword } from '@nextcloud/password-confirmation'
+import { print } from '../service/PrintService.js'
+import NcButton from '@nextcloud/vue/components/NcButton'
+import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
+
+import '@nextcloud/password-confirmation/dist/style.css'
+
+export default {
+ name: 'PersonalSettings',
+ components: {
+ NcButton,
+ NcLoadingIcon,
+ },
+ data() {
+ return {
+ generatingCodes: false,
+ }
+ },
+ computed: {
+ downloadUrl() {
+ if (!this.codes) {
+ return ''
+ }
+ return 'data:text/plain,' + encodeURIComponent(this.codes.reduce((prev, code) => {
+ return prev + code + '\r\n'
+ }, ''))
+ },
+ downloadFilename() {
+ const name = OC.theme.name || 'Nextcloud'
+ return name + '-backup-codes.txt'
+ },
+ enabled() {
+ return this.$store.state.enabled
+ },
+ total() {
+ return this.$store.state.total
+ },
+ used() {
+ return this.$store.state.used
+ },
+ codes() {
+ return this.$store.state.codes
+ },
+ name() {
+ return OC.theme.name || 'Nextcloud'
+ },
+ haveCodes() {
+ return this.codes && this.codes.length > 0
+ },
+ },
+ methods: {
+ generateBackupCodes() {
+ confirmPassword().then(() => {
+ // Hide old codes
+ this.generatingCodes = true
+
+ this.$store.dispatch('generate').then(() => {
+ this.generatingCodes = false
+ }).catch(err => {
+ OC.Notification.showTemporary(t('twofactor_backupcodes', 'An error occurred while generating your backup codes'))
+ this.generatingCodes = false
+ throw err
+ })
+ }).catch(console.error.bind(this))
+ },
+
+ getPrintData(codes) {
+ if (!codes) {
+ return ''
+ }
+ return codes.reduce((prev, code) => {
+ return prev + code + '<br>'
+ }, '')
+ },
+
+ printCodes() {
+ print(this.getPrintData(this.codes))
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.backupcodes-settings {
+ &__codes {
+ &__code {
+ font-family: monospace;
+ letter-spacing: 0.02em;
+ font-size: 1.2em;
+ }
+ }
+
+ &__actions {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--default-grid-baseline);
+ }
+}
+</style>