nextcloud-server/apps/settings/src/store/authtoken.ts

216 lines
5.3 KiB
TypeScript

/**
* @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
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
},
},
})