Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>pull/42334/head
parent
0dcea036a7
commit
960bec949a
@ -0,0 +1,114 @@ |
||||
<!-- |
||||
- @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
- |
||||
- @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
- @author Ferdinand Thiessen <opensource@fthiessen.de> |
||||
- |
||||
- @license GNU AGPL version 3 or any later version |
||||
- |
||||
- This program is free software: you can redistribute it and/or modify |
||||
- it under the terms of the GNU Affero General Public License as |
||||
- published by the Free Software Foundation, either version 3 of the |
||||
- License, or (at your option) any later version. |
||||
- |
||||
- This program is distributed in the hope that it will be useful, |
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
- GNU Affero General Public License for more details. |
||||
- |
||||
- You should have received a copy of the GNU Affero General Public License |
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
--> |
||||
|
||||
<template> |
||||
<form id="generate-app-token-section" |
||||
class="row spacing" |
||||
@submit.prevent="submit"> |
||||
<!-- Port to TextField component when available --> |
||||
<NcTextField :value.sync="deviceName" |
||||
type="text" |
||||
:maxlength="120" |
||||
:disabled="loading" |
||||
class="app-name-text-field" |
||||
:label="t('settings', 'App name')" |
||||
:placeholder="t('settings', 'App name')" /> |
||||
<NcButton type="primary" |
||||
:disabled="loading || deviceName.length === 0" |
||||
native-type="submit"> |
||||
{{ t('settings', 'Create new app password') }} |
||||
</NcButton> |
||||
|
||||
<AuthTokenSetupDialog :token="newToken" @close="newToken = null" /> |
||||
</form> |
||||
</template> |
||||
|
||||
<script lang="ts"> |
||||
import { showError } from '@nextcloud/dialogs' |
||||
import { translate as t } from '@nextcloud/l10n' |
||||
import { defineComponent } from 'vue' |
||||
import { useAuthTokenStore, type ITokenResponse } from '../store/authtoken' |
||||
|
||||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' |
||||
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' |
||||
|
||||
import AuthTokenSetupDialog from './AuthTokenSetupDialog.vue' |
||||
import logger from '../logger' |
||||
|
||||
export default defineComponent({ |
||||
name: 'AuthTokenSetup', |
||||
components: { |
||||
NcButton, |
||||
NcTextField, |
||||
AuthTokenSetupDialog, |
||||
}, |
||||
setup() { |
||||
const authTokenStore = useAuthTokenStore() |
||||
return { authTokenStore } |
||||
}, |
||||
data() { |
||||
return { |
||||
deviceName: '', |
||||
loading: false, |
||||
newToken: null as ITokenResponse|null, |
||||
} |
||||
}, |
||||
methods: { |
||||
t, |
||||
reset() { |
||||
this.loading = false |
||||
this.deviceName = '' |
||||
this.newToken = null |
||||
}, |
||||
async submit() { |
||||
try { |
||||
this.loading = true |
||||
this.newToken = await this.authTokenStore.addToken(this.deviceName) |
||||
} catch (error) { |
||||
logger.error(error as Error) |
||||
showError(t('settings', 'Error while creating device token')) |
||||
this.reset() |
||||
} finally { |
||||
this.loading = false |
||||
} |
||||
}, |
||||
}, |
||||
}) |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.app-name-text-field { |
||||
height: 44px !important; |
||||
padding-left: 12px; |
||||
margin-right: 12px; |
||||
width: 200px; |
||||
} |
||||
|
||||
.row { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.spacing { |
||||
padding-top: 16px; |
||||
} |
||||
</style> |
@ -0,0 +1,220 @@ |
||||
<!-- |
||||
- @copyright 2023 Ferdinand Thiessen <opensource@fthiessen.de> |
||||
- |
||||
- @author Ferdinand Thiessen <opensource@fthiessen.de> |
||||
- |
||||
- @license AGPL-3.0-or-later |
||||
- |
||||
- This program is free software: you can redistribute it and/or modify |
||||
- it under the terms of the GNU Affero General Public License as |
||||
- published by the Free Software Foundation, either version 3 of the |
||||
- License, or (at your option) any later version. |
||||
- |
||||
- This program is distributed in the hope that it will be useful, |
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
- GNU Affero General Public License for more details. |
||||
- |
||||
- You should have received a copy of the GNU Affero General Public License |
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
- |
||||
--> |
||||
<template> |
||||
<NcDialog :open.sync="open" |
||||
:name="t('settings', 'New app password')" |
||||
content-classes="token-dialog"> |
||||
<p> |
||||
{{ t('settings', 'Use the credentials below to configure your app or device. For security reasons this password will only be shown once.') }} |
||||
</p> |
||||
<div class="token-dialog__name"> |
||||
<NcTextField :label="t('settings', 'Username')" :value="loginName" readonly /> |
||||
<NcButton type="tertiary" |
||||
:title="copyLoginNameLabel" |
||||
:aria-label="copyLoginNameLabel" |
||||
@click="copyLoginName"> |
||||
<template #icon> |
||||
<NcIconSvgWrapper :path="copyNameIcon" /> |
||||
</template> |
||||
</NcButton> |
||||
</div> |
||||
<div class="token-dialog__password"> |
||||
<NcTextField ref="appPassword" |
||||
:label="t('settings', 'Password')" |
||||
:value="appPassword" |
||||
readonly /> |
||||
<NcButton type="tertiary" |
||||
:title="copyPasswordLabel" |
||||
:aria-label="copyPasswordLabel" |
||||
@click="copyPassword"> |
||||
<template #icon> |
||||
<NcIconSvgWrapper :path="copyPasswordIcon" /> |
||||
</template> |
||||
</NcButton> |
||||
</div> |
||||
<div class="token-dialog__qrcode"> |
||||
<NcButton v-if="!showQRCode" @click="showQRCode = true"> |
||||
{{ t('settings', 'Show QR code for mobile apps') }} |
||||
</NcButton> |
||||
<QR v-else :value="qrUrl" /> |
||||
</div> |
||||
</NcDialog> |
||||
</template> |
||||
|
||||
<script lang="ts"> |
||||
import type { ITokenResponse } from '../store/authtoken' |
||||
|
||||
import { mdiCheck, mdiContentCopy } from '@mdi/js' |
||||
import { showError } from '@nextcloud/dialogs' |
||||
import { translate as t } from '@nextcloud/l10n' |
||||
import { getRootUrl } from '@nextcloud/router' |
||||
import { defineComponent, type PropType } from 'vue' |
||||
|
||||
import QR from '@chenfengyuan/vue-qrcode' |
||||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' |
||||
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js' |
||||
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js' |
||||
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' |
||||
|
||||
import logger from '../logger' |
||||
|
||||
export default defineComponent({ |
||||
name: 'AuthTokenSetupDialog', |
||||
components: { |
||||
NcButton, |
||||
NcDialog, |
||||
NcIconSvgWrapper, |
||||
NcTextField, |
||||
QR, |
||||
}, |
||||
props: { |
||||
token: { |
||||
type: Object as PropType<ITokenResponse|null>, |
||||
required: false, |
||||
default: null, |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
isNameCopied: false, |
||||
isPasswordCopied: false, |
||||
showQRCode: false, |
||||
} |
||||
}, |
||||
computed: { |
||||
open: { |
||||
get() { |
||||
return this.token !== null |
||||
}, |
||||
set(value: boolean) { |
||||
if (!value) { |
||||
this.$emit('close') |
||||
} |
||||
}, |
||||
}, |
||||
copyPasswordIcon() { |
||||
return this.isPasswordCopied ? mdiCheck : mdiContentCopy |
||||
}, |
||||
copyNameIcon() { |
||||
return this.isNameCopied ? mdiCheck : mdiContentCopy |
||||
}, |
||||
appPassword() { |
||||
return this.token?.token ?? '' |
||||
}, |
||||
loginName() { |
||||
return this.token?.loginName ?? '' |
||||
}, |
||||
qrUrl() { |
||||
const server = window.location.protocol + '//' + window.location.host + getRootUrl() |
||||
return `nc://login/user:${this.loginName}&password:${this.appPassword}&server:${server}` |
||||
}, |
||||
copyPasswordLabel() { |
||||
if (this.isPasswordCopied) { |
||||
return t('settings', 'App password copied!') |
||||
} |
||||
return t('settings', 'Copy app password') |
||||
}, |
||||
copyLoginNameLabel() { |
||||
if (this.isNameCopied) { |
||||
return t('settings', 'Login name copied!') |
||||
} |
||||
return t('settings', 'Copy login name') |
||||
}, |
||||
}, |
||||
watch: { |
||||
token() { |
||||
// reset showing the QR code on token change |
||||
this.showQRCode = false |
||||
}, |
||||
open() { |
||||
if (this.open) { |
||||
this.$nextTick(() => { |
||||
this.$refs.appPassword!.select() |
||||
}) |
||||
} |
||||
}, |
||||
}, |
||||
methods: { |
||||
t, |
||||
async copyPassword() { |
||||
try { |
||||
await navigator.clipboard.writeText(this.appPassword) |
||||
this.isPasswordCopied = true |
||||
} catch (e) { |
||||
this.isPasswordCopied = false |
||||
logger.error(e as Error) |
||||
showError(t('settings', 'Could not copy app password. Please copy it manually.')) |
||||
} finally { |
||||
setTimeout(() => { |
||||
this.isPasswordCopied = false |
||||
}, 4000) |
||||
} |
||||
}, |
||||
async copyLoginName() { |
||||
try { |
||||
await navigator.clipboard.writeText(this.loginName) |
||||
this.isNameCopied = true |
||||
} catch (e) { |
||||
this.isNameCopied = false |
||||
logger.error(e as Error) |
||||
showError(t('settings', 'Could not copy login name. Please copy it manually.')) |
||||
} finally { |
||||
setTimeout(() => { |
||||
this.isNameCopied = false |
||||
}, 4000) |
||||
} |
||||
}, |
||||
}, |
||||
}) |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
:deep(.token-dialog) { |
||||
display: flex; |
||||
flex-direction: column; |
||||
gap: 12px; |
||||
|
||||
padding-inline: 22px; |
||||
padding-block-end: 20px; |
||||
|
||||
> * { |
||||
box-sizing: border-box; |
||||
} |
||||
} |
||||
|
||||
.token-dialog { |
||||
&__name, &__password { |
||||
align-items: end; |
||||
display: flex; |
||||
gap: 10px; |
||||
|
||||
:deep(input) { |
||||
font-family: monospace; |
||||
} |
||||
} |
||||
|
||||
&__qrcode { |
||||
display: flex; |
||||
justify-content: center; |
||||
} |
||||
} |
||||
</style> |
@ -1,239 +0,0 @@ |
||||
<!-- |
||||
- @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
- |
||||
- @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> |
||||
- |
||||
- @license GNU AGPL version 3 or any later version |
||||
- |
||||
- This program is free software: you can redistribute it and/or modify |
||||
- it under the terms of the GNU Affero General Public License as |
||||
- published by the Free Software Foundation, either version 3 of the |
||||
- License, or (at your option) any later version. |
||||
- |
||||
- This program is distributed in the hope that it will be useful, |
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
- GNU Affero General Public License for more details. |
||||
- |
||||
- You should have received a copy of the GNU Affero General Public License |
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
--> |
||||
|
||||
<template> |
||||
<div v-if="!adding" id="generate-app-token-section" class="row spacing"> |
||||
<!-- Port to TextField component when available --> |
||||
<NcTextField :value.sync="deviceName" |
||||
type="text" |
||||
:maxlength="120" |
||||
:disabled="loading" |
||||
class="app-name-text-field" |
||||
:label="t('settings', 'App name')" |
||||
:placeholder="t('settings', 'App name')" |
||||
@keydown.enter="submit" /> |
||||
<NcButton :disabled="loading || deviceName.length === 0" |
||||
type="primary" |
||||
@click="submit"> |
||||
{{ t('settings', 'Create new app password') }} |
||||
</NcButton> |
||||
</div> |
||||
<div v-else class="spacing"> |
||||
{{ t('settings', 'Use the credentials below to configure your app or device.') }} |
||||
{{ t('settings', 'For security reasons this password will only be shown once.') }} |
||||
<div class="app-password-row"> |
||||
<label for="app-username" class="app-password-label">{{ t('settings', 'Username') }}</label> |
||||
<input id="app-username" |
||||
:value="loginName" |
||||
type="text" |
||||
class="monospaced" |
||||
readonly="readonly" |
||||
@focus="selectInput"> |
||||
</div> |
||||
<div class="app-password-row"> |
||||
<label for="app-password" class="app-password-label">{{ t('settings', 'Password') }}</label> |
||||
<input id="app-password" |
||||
ref="appPassword" |
||||
:value="appPassword" |
||||
type="text" |
||||
class="monospaced" |
||||
readonly="readonly" |
||||
@focus="selectInput"> |
||||
<NcButton type="tertiary" |
||||
:title="copyTooltipOptions" |
||||
:aria-label="copyTooltipOptions" |
||||
@click="copyPassword"> |
||||
<template #icon> |
||||
<Check v-if="copied" :size="20" /> |
||||
<ContentCopy v-else :size="20" /> |
||||
</template> |
||||
</NcButton> |
||||
<NcButton @click="reset"> |
||||
{{ t('settings', 'Done') }} |
||||
</NcButton> |
||||
</div> |
||||
<div class="app-password-row"> |
||||
<span class="app-password-label" /> |
||||
<NcButton v-if="!showQR" |
||||
@click="showQR = true"> |
||||
{{ t('settings', 'Show QR code for mobile apps') }} |
||||
</NcButton> |
||||
<QR v-else |
||||
:value="qrUrl" /> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import QR from '@chenfengyuan/vue-qrcode' |
||||
import { confirmPassword } from '@nextcloud/password-confirmation' |
||||
import '@nextcloud/password-confirmation/dist/style.css' |
||||
import { showError } from '@nextcloud/dialogs' |
||||
import { getRootUrl } from '@nextcloud/router' |
||||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' |
||||
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' |
||||
|
||||
import Check from 'vue-material-design-icons/Check.vue' |
||||
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue' |
||||
|
||||
export default { |
||||
name: 'AuthTokenSetupDialogue', |
||||
components: { |
||||
Check, |
||||
ContentCopy, |
||||
NcButton, |
||||
QR, |
||||
NcTextField, |
||||
}, |
||||
props: { |
||||
add: { |
||||
type: Function, |
||||
required: true, |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
adding: false, |
||||
loading: false, |
||||
deviceName: '', |
||||
appPassword: '', |
||||
loginName: '', |
||||
copied: false, |
||||
showQR: false, |
||||
qrUrl: '', |
||||
} |
||||
}, |
||||
computed: { |
||||
copyTooltipOptions() { |
||||
if (this.copied) { |
||||
return t('settings', 'Copied!') |
||||
} |
||||
return t('settings', 'Copy') |
||||
}, |
||||
}, |
||||
methods: { |
||||
selectInput(e) { |
||||
e.currentTarget.select() |
||||
}, |
||||
submit() { |
||||
confirmPassword() |
||||
.then(() => { |
||||
this.loading = true |
||||
return this.add(this.deviceName) |
||||
}) |
||||
.then(token => { |
||||
this.adding = true |
||||
this.loginName = token.loginName |
||||
this.appPassword = token.token |
||||
|
||||
const server = window.location.protocol + '//' + window.location.host + getRootUrl() |
||||
this.qrUrl = `nc://login/user:${token.loginName}&password:${token.token}&server:${server}` |
||||
|
||||
this.$nextTick(() => { |
||||
this.$refs.appPassword.select() |
||||
}) |
||||
}) |
||||
.catch(err => { |
||||
console.error('could not create a new app password', err) |
||||
OC.Notification.showTemporary(t('settings', 'Error while creating device token')) |
||||
|
||||
this.reset() |
||||
}) |
||||
}, |
||||
async copyPassword() { |
||||
try { |
||||
await navigator.clipboard.writeText(this.appPassword) |
||||
this.copied = true |
||||
} catch (e) { |
||||
this.copied = false |
||||
console.error(e) |
||||
showError(t('settings', 'Could not copy app password. Please copy it manually.')) |
||||
} finally { |
||||
setTimeout(() => { |
||||
this.copied = false |
||||
}, 4000) |
||||
} |
||||
}, |
||||
reset() { |
||||
this.adding = false |
||||
this.loading = false |
||||
this.showQR = false |
||||
this.qrUrl = '' |
||||
this.deviceName = '' |
||||
this.appPassword = '' |
||||
this.loginName = '' |
||||
}, |
||||
}, |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.app-password-row { |
||||
display: flex; |
||||
align-items: center; |
||||
flex-wrap: wrap; |
||||
margin-top: calc(var(--default-grid-baseline) * 2); |
||||
|
||||
.icon { |
||||
background-size: 16px 16px; |
||||
display: inline-block; |
||||
position: relative; |
||||
top: 3px; |
||||
margin-left: 5px; |
||||
margin-right: 8px; |
||||
} |
||||
|
||||
} |
||||
|
||||
.app-password-label { |
||||
display: table-cell; |
||||
margin-right: 1em; |
||||
text-align: left; |
||||
vertical-align: middle; |
||||
width: 100px; |
||||
} |
||||
|
||||
.app-name-text-field { |
||||
height: 44px !important; |
||||
padding-left: 12px; |
||||
margin-right: 12px; |
||||
width: 200px; |
||||
} |
||||
|
||||
.monospaced { |
||||
width: 245px; |
||||
font-family: monospace; |
||||
} |
||||
|
||||
.button-vue{ |
||||
display:inline-block; |
||||
margin: 3px 3px 3px 3px; |
||||
} |
||||
|
||||
.row { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.spacing { |
||||
padding-top: 16px; |
||||
} |
||||
</style> |
@ -0,0 +1,214 @@ |
||||
/** |
||||
* @copyright 2023 Ferdinand Thiessen <opensource@fthiessen.de> |
||||
* |
||||
* @author Ferdinand Thiessen <opensource@fthiessen.de> |
||||
* |
||||
* @license AGPL-3.0-or-later |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
import { showError } from '@nextcloud/dialogs' |
||||
import { loadState } from '@nextcloud/initial-state' |
||||
import { translate as t } from '@nextcloud/l10n' |
||||
import { confirmPassword } from '@nextcloud/password-confirmation' |
||||
import { generateUrl } from '@nextcloud/router' |
||||
import { defineStore } from 'pinia' |
||||
|
||||
import axios from '@nextcloud/axios' |
||||
import logger from '../logger' |
||||
|
||||
const BASE_URL = generateUrl('/settings/personal/authtokens') |
||||
|
||||
const confirm = () => { |
||||
return new Promise(resolve => { |
||||
window.OC.dialogs.confirm( |
||||
t('settings', 'Do you really want to wipe your data from this device?'), |
||||
t('settings', 'Confirm wipe'), |
||||
resolve, |
||||
true, |
||||
) |
||||
}) |
||||
} |
||||
|
||||
export enum TokenType { |
||||
TEMPORARY_TOKEN = 0, |
||||
PERMANENT_TOKEN = 1, |
||||
WIPING_TOKEN = 2, |
||||
} |
||||
|
||||
export interface IToken { |
||||
id: number |
||||
canDelete: boolean |
||||
canRename: boolean |
||||
current?: true |
||||
/** |
||||
* Last activity as UNIX timestamp (in seconds) |
||||
*/ |
||||
lastActivity: number |
||||
name: string |
||||
type: TokenType |
||||
scope: Record<string, boolean> |
||||
} |
||||
|
||||
export interface ITokenResponse { |
||||
/** |
||||
* The device token created |
||||
*/ |
||||
deviceToken: IToken |
||||
/** |
||||
* User who is assigned with this token |
||||
*/ |
||||
loginName: string |
||||
/** |
||||
* The token for authentication |
||||
*/ |
||||
token: string |
||||
} |
||||
|
||||
export const useAuthTokenStore = defineStore('auth-token', { |
||||
state() { |
||||
return { |
||||
tokens: loadState<IToken[]>('settings', 'app_tokens', []), |
||||
} |
||||
}, |
||||
actions: { |
||||
/** |
||||
* Update a token on server |
||||
* @param token Token to update |
||||
*/ |
||||
async updateToken(token: IToken) { |
||||
const { data } = await axios.put(`${BASE_URL}/${token.id}`, token) |
||||
return data |
||||
}, |
||||
|
||||
/** |
||||
* Add a new token |
||||
* @param name The token name |
||||
*/ |
||||
async addToken(name: string) { |
||||
logger.debug('Creating a new app token') |
||||
|
||||
try { |
||||
await confirmPassword() |
||||
|
||||
const { data } = await axios.post<ITokenResponse>(BASE_URL, { name }) |
||||
this.tokens.push(data.deviceToken) |
||||
logger.debug('App token created') |
||||
return data |
||||
} catch (error) { |
||||
return null |
||||
} |
||||
}, |
||||
|
||||
/** |
||||
* Delete a given app token |
||||
* @param token Token to delete |
||||
*/ |
||||
async deleteToken(token: IToken) { |
||||
logger.debug('Deleting app token', { token }) |
||||
|
||||
this.tokens = this.tokens.filter(({ id }) => id !== token.id) |
||||
|
||||
try { |
||||
await axios.delete(`${BASE_URL}/${token.id}`) |
||||
logger.debug('App token deleted') |
||||
return true |
||||
} catch (error) { |
||||
logger.error('Could not delete app token', { error }) |
||||
showError(t('settings', 'Could not delete the app token')) |
||||
// Restore
|
||||
this.tokens.push(token) |
||||
} |
||||
return false |
||||
}, |
||||
|
||||
/** |
||||
* Wipe a token and the connected device |
||||
* @param token Token to wipe |
||||
*/ |
||||
async wipeToken(token: IToken) { |
||||
logger.debug('Wiping app token', { token }) |
||||
|
||||
try { |
||||
await confirmPassword() |
||||
|
||||
if (!(await confirm())) { |
||||
logger.debug('Wipe aborted by user') |
||||
return |
||||
} |
||||
|
||||
await axios.post(`${BASE_URL}/wipe/${token.id}`) |
||||
logger.debug('App token marked for wipe', { token }) |
||||
|
||||
token.type = TokenType.WIPING_TOKEN |
||||
return true |
||||
} catch (error) { |
||||
logger.error('Could not wipe app token', { error }) |
||||
showError(t('settings', 'Error while wiping the device with the token')) |
||||
} |
||||
return false |
||||
}, |
||||
|
||||
/** |
||||
* Rename an existing token |
||||
* @param token The token to rename |
||||
* @param newName The new name to set |
||||
*/ |
||||
async renameToken(token: IToken, newName: string) { |
||||
logger.debug(`renaming app token ${token.id} from ${token.name} to '${newName}'`) |
||||
|
||||
const oldName = token.name |
||||
token.name = newName |
||||
|
||||
try { |
||||
await this.updateToken(token) |
||||
logger.debug('App token name updated') |
||||
return true |
||||
} catch (error) { |
||||
logger.error('Could not update app token name', { error }) |
||||
showError(t('settings', 'Error while updating device token name')) |
||||
// Restore
|
||||
token.name = oldName |
||||
} |
||||
return false |
||||
}, |
||||
|
||||
/** |
||||
* Set scope of the token |
||||
* @param token Token to set scope |
||||
* @param scope scope to set |
||||
* @param value value to set |
||||
*/ |
||||
async setTokenScope(token: IToken, scope: string, value: boolean) { |
||||
logger.debug('Updating app token scope', { token, scope, value }) |
||||
|
||||
const oldVal = token.scope[scope] |
||||
token.scope[scope] = value |
||||
|
||||
try { |
||||
await this.updateToken(token) |
||||
logger.debug('app token scope updated') |
||||
return true |
||||
} catch (error) { |
||||
logger.error('could not update app token scope', { error }) |
||||
showError(t('settings', 'Error while updating device token scope')) |
||||
// Restore
|
||||
token.scope[scope] = oldVal |
||||
} |
||||
return false |
||||
}, |
||||
}, |
||||
|
||||
}) |
Loading…
Reference in new issue