/** * @copyright 2023 Ferdinand Thiessen * * @author Ferdinand Thiessen * * @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 . * */ 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 } 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('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(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 token.canRename = false // wipe tokens can not be renamed 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 }, }, })