feat(api tokens): validate title field when creating a new token

This commit is contained in:
kolaente 2023-09-01 12:56:23 +02:00
parent e47ad021a3
commit 021f92303d
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
4 changed files with 29 additions and 10 deletions

View File

@ -150,8 +150,10 @@
"60d": "60 Days", "60d": "60 Days",
"90d": "90 Days", "90d": "90 Days",
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.", "permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
"titleRequired": "The title is required",
"attributes": { "attributes": {
"title": "Title", "title": "Title",
"titlePlaceholder": "Enter a title you will recognize later",
"expiresAt": "Expires at", "expiresAt": "Expires at",
"permissions": "Permissions" "permissions": "Permissions"
} }

View File

@ -6,6 +6,7 @@ export interface IApiPermission {
export interface IApiToken extends IAbstract { export interface IApiToken extends IAbstract {
id: number id: number
title: string
token: string token: string
permissions: IApiPermission permissions: IApiPermission
expiresAt: Date expiresAt: Date

View File

@ -3,6 +3,7 @@ import type {IApiToken} from '@/modelTypes/IApiToken'
export default class ApiTokenModel extends AbstractModel<IApiToken> { export default class ApiTokenModel extends AbstractModel<IApiToken> {
id = 0 id = 0
title = ''
token = '' token = ''
permissions = null permissions = null
expiresAt: Date = null expiresAt: Date = null

View File

@ -16,6 +16,8 @@ const availableRoutes = ref(null)
const newToken = ref(new ApiTokenModel()) const newToken = ref(new ApiTokenModel())
const newTokenExpiry = ref<string | number>(30) const newTokenExpiry = ref<string | number>(30)
const newTokenPermissions = ref({}) const newTokenPermissions = ref({})
const newTokenTitleValid = ref(true)
const apiTokenTitle = ref()
onMounted(async () => { onMounted(async () => {
tokens.value = await service.getAll() tokens.value = await service.getAll()
@ -38,12 +40,17 @@ function deleteToken() {
} }
async function createToken() { async function createToken() {
if (!newTokenTitleValid.value) {
apiTokenTitle.value.focus()
return
}
const expiry = Number(newTokenExpiry.value) const expiry = Number(newTokenExpiry.value)
if(!isNaN(expiry)) { if (!isNaN(expiry)) {
// if it's a number, we assume it's the number of days in the future // if it's a number, we assume it's the number of days in the future
newToken.value.expiresAt = new Date((new Date()) + expiry * MILLISECONDS_A_DAY) newToken.value.expiresAt = new Date((new Date()) + expiry * MILLISECONDS_A_DAY)
} }
newToken.value.permissions = {} newToken.value.permissions = {}
Object.entries(newTokenPermissions.value).forEach(([key, ps]) => { Object.entries(newTokenPermissions.value).forEach(([key, ps]) => {
const all = Object.entries(ps) const all = Object.entries(ps)
@ -54,7 +61,7 @@ async function createToken() {
newToken.value.permissions[key] = all newToken.value.permissions[key] = all
} }
}) })
const token = await service.create(newToken.value) const token = await service.create(newToken.value)
newToken.value = new ApiTokenModel() newToken.value = new ApiTokenModel()
newTokenExpiry.value = 30 newTokenExpiry.value = 30
@ -113,19 +120,27 @@ async function createToken() {
<input <input
class="input" class="input"
id="apiTokenTitle" id="apiTokenTitle"
ref="apiTokenTitle"
type="text" type="text"
v-focus v-focus
v-model="newToken.title"/> :placeholder="$t('user.settings.apiTokens.attributes.titlePlaceholder')"
v-model="newToken.title"
@keyup="() => newTokenTitleValid = newToken.title !== ''"
@focusout="() => newTokenTitleValid = newToken.title !== ''"
/>
</div> </div>
<p class="help is-danger" v-if="!newTokenTitleValid">
{{ $t('user.settings.apiTokens.titleRequired') }}
</p>
</div> </div>
<!-- Expiry --> <!-- Expiry -->
<div class="field"> <div class="field">
<label class="label" for="apiTokenTitle">{{ <label class="label" for="apiTokenExpiry">
$t('user.settings.apiTokens.attributes.expiresAt') {{ $t('user.settings.apiTokens.attributes.expiresAt') }}
}}</label> </label>
<div class="control select"> <div class="control select">
<select class="select" v-model="newTokenExpiry"> <select class="select" v-model="newTokenExpiry" id="apiTokenExpiry">
<option value="30">{{ $t('user.settings.apiTokens.30d') }}</option> <option value="30">{{ $t('user.settings.apiTokens.30d') }}</option>
<option value="60">{{ $t('user.settings.apiTokens.60d') }}</option> <option value="60">{{ $t('user.settings.apiTokens.60d') }}</option>
<option value="90">{{ $t('user.settings.apiTokens.90d') }}</option> <option value="90">{{ $t('user.settings.apiTokens.90d') }}</option>
@ -140,9 +155,9 @@ async function createToken() {
<p>{{ $t('user.settings.apiTokens.permissionExplanation') }}</p> <p>{{ $t('user.settings.apiTokens.permissionExplanation') }}</p>
<div v-for="(routes, group) in availableRoutes" class="mb-2" :key="group"> <div v-for="(routes, group) in availableRoutes" class="mb-2" :key="group">
<strong>{{ group }}</strong><br/> <strong>{{ group }}</strong><br/>
<fancycheckbox <fancycheckbox
v-for="(paths, route) in routes" v-for="(paths, route) in routes"
:key="group+'-'+route" :key="group+'-'+route"
class="mr-2" class="mr-2"
v-model="newTokenPermissions[group][route]" v-model="newTokenPermissions[group][route]"
> >