refactor(auth): enforce team member authorization across app

Restrict sensitive operations to admins/owners and hide sensitive data
from team members:
- Add authorization checks to Livewire components and API endpoints
- Restrict team members from accessing sensitive permissions and data
- Hide environment variable values from non-admin team members
- Update policies to enforce team-level admin status requirement
- Add useSensitivePermissions policy for read:sensitive tokens
- Improve disabled button UX with auth-specific tooltips
- Add authorization checks in middleware for API tokens

Closes authorization gaps in project management, server management,
and settings components.
This commit is contained in:
Andras Bacsai committed 2026-02-27 11:41:01 +01:00
1 parent e82942b387
commit b878dc8102
49 files changed
+919 -136

No files matched your search

+5 -1
View File
@@ -122,7 +122,11 @@
}
@utility button {
@apply flex gap-2 justify-center items-center px-2 h-8 text-sm text-black normal-case rounded-sm border-2 outline-0 cursor-pointer font-medium bg-white border-neutral-200 hover:bg-neutral-100 dark:bg-coolgray-100 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-200 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-neutral-600 disabled:border-transparent disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning focus-visible:ring-offset-2 dark:focus-visible:ring-offset-base;
@apply flex gap-2 justify-center items-center px-2 h-8 text-sm text-black normal-case rounded-sm border-2 outline-0 cursor-pointer font-medium bg-white border-neutral-200 hover:bg-neutral-100 dark:bg-coolgray-100 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-200 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-neutral-600 disabled:border-neutral-200 dark:disabled:border-coolgray-300 disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-coollabs dark:focus-visible:ring-warning focus-visible:ring-offset-2 dark:focus-visible:ring-offset-base;
}
@utility auth-tooltip {
@apply fixed z-[99] px-2.5 py-1.5 text-xs rounded-sm pointer-events-none whitespace-nowrap text-neutral-700 bg-neutral-200 dark:text-neutral-300 dark:bg-coolgray-400;
}
@utility alert-success {
@@ -1,3 +1,24 @@
@if ($authDisabled)
<span class="relative inline-flex"
x-data="{ visible: false, _t: null }"
@mouseenter="_t = setTimeout(() => {
visible = true;
$nextTick(() => requestAnimationFrame(() => {
const tip = $refs.tip;
if (!tip) return;
const r = $el.getBoundingClientRect();
const t = tip.getBoundingClientRect();
let top = r.top - t.height - 6;
let left = r.left;
if (top < 4) top = r.bottom + 6;
if (left + t.width > innerWidth - 8) left = innerWidth - 8 - t.width;
if (left < 4) left = 4;
tip.style.top = top + 'px';
tip.style.left = left + 'px';
}));
}, 300)"
@mouseleave="clearTimeout(_t); visible = false">
@endif
<button @disabled($disabled) {{ $attributes->merge(['class' => $defaultClass]) }}
{{ $attributes->merge(['type' => 'button']) }}
@isset($confirm)
@@ -18,3 +39,9 @@
@endif
@endif
</button>
@if ($authDisabled)
<div x-ref="tip" x-show="visible" x-cloak class="auth-tooltip">
You do not have permission to perform this action.
</div>
</span>
@endif
@@ -6,6 +6,7 @@
'buttonFullWidth' => false,
'customButton' => null,
'disabled' => false,
'authDisabled' => false,
'dispatchAction' => false,
'submitAction' => 'delete',
'content' => null,
@@ -145,11 +146,11 @@
@else
@if ($disabled)
@if ($buttonFullWidth)
<x-forms.button class="w-full" isError disabled wire:target>
<x-forms.button class="w-full" isError disabled :authDisabled="$authDisabled" wire:target>
{{ $buttonTitle }}
</x-forms.button>
@else
<x-forms.button isError disabled wire:target>
<x-forms.button isError disabled :authDisabled="$authDisabled" wire:target>
{{ $buttonTitle }}
</x-forms.button>
@endif
@@ -246,6 +246,7 @@
class="block w-full px-4 py-2 text-left text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-coolgray-300">
Download displayed logs
</button>
@can('update', $application)
<button x-on:click="
downloadingAllLogs = true;
$wire.downloadAllLogs().then(logs => {
@@ -276,6 +277,7 @@
Downloading...
</span>
</button>
@endcan
</div>
</div>
</div>
@@ -85,7 +85,7 @@
</x-forms.button>
@endif
@endif
<x-modal-confirmation :disabled="!auth()->user()->can('deploy', $application)" title="Confirm Application Stopping?" buttonTitle="Stop"
<x-modal-confirmation :disabled="!auth()->user()->can('deploy', $application)" :authDisabled="!auth()->user()->can('deploy', $application)" title="Confirm Application Stopping?" buttonTitle="Stop"
submitAction="stop" :checkboxes="$checkboxes" :actions="[
'This application will be stopped.',
'All non-persistent data of this application will be deleted.',
@@ -40,7 +40,7 @@
@if ($database->destination->server->isFunctional())
<div class="flex flex-wrap gap-2 items-center">
@if (!str($database->status)->startsWith('exited'))
<x-modal-confirmation :disabled="!auth()->user()->can('manage', $database)" title="Confirm Database Restart?" buttonTitle="Restart" submitAction="restart"
<x-modal-confirmation :disabled="!auth()->user()->can('manage', $database)" :authDisabled="!auth()->user()->can('manage', $database)" title="Confirm Database Restart?" buttonTitle="Restart" submitAction="restart"
:actions="[
'This database will be unavailable during the restart.',
'If the database is currently in use data could be lost.',
@@ -58,7 +58,7 @@
Restart
</x-slot:button-title>
</x-modal-confirmation>
<x-modal-confirmation :disabled="!auth()->user()->can('manage', $database)" title="Confirm Database Stopping?" buttonTitle="Stop" submitAction="stop"
<x-modal-confirmation :disabled="!auth()->user()->can('manage', $database)" :authDisabled="!auth()->user()->can('manage', $database)" title="Confirm Database Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[
'This database will be stopped.',
'If the database is currently in use data could be lost.',
@@ -80,7 +80,7 @@
</x-slot:button-title>
</x-modal-confirmation>
@else
<button @disabled(!auth()->user()->can('manage', $database)) @click="$wire.dispatch('startEvent')" class="gap-2 button">
<x-forms.button canGate="manage" :canResource="$database" @click="$wire.dispatch('startEvent')" class="gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
@@ -88,17 +88,15 @@
<path d="M7 4v16l13 -8z" />
</svg>
Start
</button>
</x-forms.button>
@endif
@script
<script>
$wire.$on('startEvent', () => {
window.dispatchEvent(new CustomEvent('startdatabase'));
$wire.$call('start');
});
$wire.$on('restartEvent', () => {
$wire.$dispatch('info', 'Restarting database.');
window.dispatchEvent(new CustomEvent('startdatabase'));
$wire.$call('restart');
});
</script>
@@ -40,7 +40,7 @@
</svg>
Restart
</x-forms.button>
<x-modal-confirmation :disabled="!auth()->user()->can('stop', $service)" title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
<x-modal-confirmation :disabled="!auth()->user()->can('stop', $service)" :authDisabled="!auth()->user()->can('stop', $service)" title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Confirm">
<x-slot:button-title>
@@ -68,7 +68,7 @@
</svg>
Restart
</x-forms.button>
<x-modal-confirmation :disabled="!auth()->user()->can('stop', $service)" title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
<x-modal-confirmation :disabled="!auth()->user()->can('stop', $service)" :authDisabled="!auth()->user()->can('stop', $service)" title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Confirm">
<x-slot:button-title>
@@ -86,7 +86,7 @@
</x-slot:button-title>
</x-modal-confirmation>
@elseif (str($service->status)->contains('exited'))
<button @disabled(!auth()->user()->can('deploy', $service)) @click="$wire.dispatch('startEvent')" class="gap-2 button">
<x-forms.button canGate="deploy" :canResource="$service" @click="$wire.dispatch('startEvent')" class="gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
@@ -94,9 +94,9 @@
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
</button>
</x-forms.button>
@else
<x-modal-confirmation :disabled="!auth()->user()->can('stop', $service)" title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
<x-modal-confirmation :disabled="!auth()->user()->can('stop', $service)" :authDisabled="!auth()->user()->can('stop', $service)" title="Confirm Service Stopping?" buttonTitle="Stop" :dispatchEvent="true"
submitAction="stop" dispatchEventType="stopEvent" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]"
:confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" step2ButtonText="Confirm">
<x-slot:button-title>
@@ -113,7 +113,7 @@
Stop
</x-slot:button-title>
</x-modal-confirmation>
<button @disabled(!auth()->user()->can('deploy', $service)) @click="$wire.dispatch('startEvent')" class="gap-2 button">
<x-forms.button canGate="deploy" :canResource="$service" @click="$wire.dispatch('startEvent')" class="gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
@@ -121,7 +121,7 @@
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
</button>
</x-forms.button>
@endif
</div>
@else
@@ -149,11 +149,9 @@
);
return;
}
window.dispatchEvent(new CustomEvent('startservice'));
$wire.$call('start');
});
$wire.$on('forceDeployEvent', () => {
window.dispatchEvent(new CustomEvent('startservice'));
$wire.$call('forceDeploy');
});
$wire.$on('restartEvent', async () => {
@@ -166,12 +164,10 @@
}
$wire.$dispatch('info',
'Gracefully stopping service.<br/><br/>It could take a while depending on the service.');
window.dispatchEvent(new CustomEvent('startservice'));
$wire.$call('restart');
});
$wire.$on('pullAndRestartEvent', () => {
$wire.$dispatch('info', 'Pulling new images and restarting service.');
window.dispatchEvent(new CustomEvent('startservice'));
$wire.$call('pullAndRestartEvent');
});
$wire.on('imagePulled', () => {
@@ -139,15 +139,22 @@
@else
<div class="flex flex-col w-full gap-2 lg:flex-row">
<x-forms.input disabled id="key" />
<x-forms.env-var-input
disabled
type="password"
id="value"
:availableVars="$this->availableSharedVariables"
:projectUuid="data_get($parameters, 'project_uuid')"
:environmentUuid="data_get($parameters, 'environment_uuid')" />
@if ($is_shared)
<x-forms.input disabled type="password" id="real_value" />
@if ($isValueHidden)
<div class="w-full">
<input disabled type="text" value="Hidden (only admins can view)"
class="input italic !text-neutral-500 dark:!text-neutral-500" />
</div>
@else
<x-forms.env-var-input
disabled
type="password"
id="value"
:availableVars="$this->availableSharedVariables"
:projectUuid="data_get($parameters, 'project_uuid')"
:environmentUuid="data_get($parameters, 'environment_uuid')" />
@if ($is_shared)
<x-forms.input disabled type="password" id="real_value" />
@endif
@endif
</div>
@endcan
@@ -3,7 +3,7 @@
<h2>Healthchecks</h2>
<x-forms.button canGate="update" :canResource="$resource" type="submit">Save</x-forms.button>
@if (!$healthCheckEnabled)
<x-modal-confirmation :disabled="!auth()->user()->can('update', $resource)" title="Confirm Healthcheck Enable?" buttonTitle="Enable Healthcheck"
<x-modal-confirmation :disabled="!auth()->user()->can('update', $resource)" :authDisabled="!auth()->user()->can('update', $resource)" title="Confirm Healthcheck Enable?" buttonTitle="Enable Healthcheck"
submitAction="toggleHealthcheck" :actions="['Enable healthcheck for this resource.']"
warningMessage="If the health check fails, your application will become inaccessible. Please review the <a href='https://coolify.io/docs/knowledge-base/health-checks' target='_blank' class='underline text-white'>Health Checks</a> guide before proceeding!"
step2ButtonText="Enable Healthcheck" :confirmWithText="false" :confirmWithPassword="false"
@@ -13,36 +13,43 @@
<div class="flex flex-col gap-2 pb-2">
<div class="flex gap-2 items-end">
<h2>Scheduled Task</h2>
<x-forms.button type="submit">
<x-forms.button canGate="update" :canResource="$resource" type="submit">
Save
</x-forms.button>
@if ($resource->isRunning())
<x-forms.button type="button" wire:click="executeNow">
Execute Now
</x-forms.button>
@can('update', $resource)
<x-forms.button type="button" wire:click="executeNow">
Execute Now
</x-forms.button>
@endcan
@endif
<x-modal-confirmation title="Confirm Scheduled Task Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $task->id }})" :actions="['The selected scheduled task will be permanently deleted.']" confirmationText="{{ $task->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Scheduled Task Name below"
shortConfirmationLabel="Scheduled Task Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
@can('update', $resource)
<x-modal-confirmation title="Confirm Scheduled Task Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $task->id }})" :actions="['The selected scheduled task will be permanently deleted.']" confirmationText="{{ $task->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Scheduled Task Name below"
shortConfirmationLabel="Scheduled Task Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
@endcan
</div>
<div class="w-48">
<x-forms.checkbox instantSave id="isEnabled" label="Enabled" />
@can('update', $resource)
<x-forms.checkbox instantSave id="isEnabled" label="Enabled" />
@else
<x-forms.checkbox disabled id="isEnabled" label="Enabled" />
@endcan
</div>
<div class="flex gap-2 w-full">
<x-forms.input placeholder="Name" id="name" label="Name" required />
<x-forms.input placeholder="php artisan schedule:run" id="command" label="Command" required />
<x-forms.input placeholder="0 0 * * * or daily" id="frequency" label="Frequency" required />
<x-forms.input type="number" placeholder="300" id="timeout"
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="Name" id="name" label="Name" required />
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="php artisan schedule:run" id="command" label="Command" required />
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="0 0 * * * or daily" id="frequency" label="Frequency" required />
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" type="number" placeholder="300" id="timeout"
helper="Maximum execution time in seconds (60-36000)." label="Timeout (seconds)" required />
@if ($type === 'application')
<x-forms.input placeholder="php"
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="php"
helper="You can leave this empty if your resource only has one container." id="container"
label="Container name" />
@elseif ($type === 'service')
<x-forms.input placeholder="php"
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="php"
helper="You can leave this empty if your resource only has one service in your stack. Otherwise use the stack name, without the random generated ID. So if you have a mysql service in your stack, use mysql."
id="container" label="Service name" />
@endif
@@ -18,21 +18,23 @@
<x-forms.input required id="description" label="Description" />
<x-forms.button type="submit">Create</x-forms.button>
</div>
<div class="flex">
Permissions
<x-helper class="px-1" helper="These permissions will be granted to the token." /><span
class="pr-1">:</span>
<div class="flex gap-1 font-bold dark:text-white">
@if ($permissions)
<div class="flex items-center gap-2">
<span>Permissions</span>
<x-helper helper="These permissions will be granted to the token." />
@if ($permissions)
<div class="flex gap-1.5 flex-wrap">
@foreach ($permissions as $permission)
<div>{{ $permission }}</div>
<span
class="px-2 py-0.5 text-xs rounded-sm font-medium {{ $permission === 'root' ? 'bg-red-500/20 text-red-400' : ($permission === 'write' || $permission === 'write:sensitive' ? 'bg-amber-500/20 text-amber-400' : ($permission === 'deploy' ? 'bg-blue-500/20 text-blue-400' : 'bg-neutral-500/20 text-neutral-300')) }}">
{{ $permission }}
</span>
@endforeach
@endif
</div>
</div>
@endif
</div>
<h4>Token Permissions</h4>
<div class="w-64">
<div class="w-96">
@if ($canUseRootPermissions)
<x-forms.checkbox label="root" wire:model.live="permissions" domValue="root"
helper="Root access, be careful!" :checked="in_array('root', $permissions)"></x-forms.checkbox>
@@ -59,9 +61,14 @@
@endif
<x-forms.checkbox label="read" domValue="read" wire:model.live="permissions" domValue="read"
:checked="in_array('read', $permissions)"></x-forms.checkbox>
<x-forms.checkbox label="read:sensitive" wire:model.live="permissions" domValue="read:sensitive"
helper="Responses will include secrets, logs, passwords, and compose file contents."
:checked="in_array('read:sensitive', $permissions)"></x-forms.checkbox>
@if ($canUseSensitivePermissions)
<x-forms.checkbox label="read:sensitive" wire:model.live="permissions" domValue="read:sensitive"
helper="Responses will include secrets, logs, passwords, and compose file contents."
:checked="in_array('read:sensitive', $permissions)"></x-forms.checkbox>
@else
<x-forms.checkbox label="read:sensitive (admin/owner only)" disabled domValue="read:sensitive"
helper="Read:sensitive access requires admin or owner role" :checked="false"></x-forms.checkbox>
@endif
@endif
</div>
@if (in_array('root', $permissions))
@@ -70,44 +77,74 @@
</form>
@endcan
@if (session()->has('token'))
<div class="py-4 font-bold dark:text-warning">Please copy this token now. For your security, it won't be shown
again.
<div class="p-4 my-4 border rounded dark:border-coolgray-200 dark:bg-coolgray-100">
<div class="pb-2 font-bold dark:text-warning">Please copy this token now. For your security, it won't
be shown again.</div>
<div class="relative" x-data="{ copied: false, isSecure: window.isSecureContext }">
<input type="text" value="{{ session('token') }}" readonly
class="input !text-white !bg-coolgray-200 font-mono" />
<button x-show="isSecure"
@click.prevent="copied = true; navigator.clipboard.writeText({{ Js::from(session('token')) }}); setTimeout(() => copied = false, 1000)"
class="absolute right-2 top-1/2 -translate-y-1/2 p-1.5 text-gray-400 hover:text-gray-300 transition-colors"
title="Copy to clipboard">
<svg x-show="!copied" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
<svg x-show="copied" class="w-5 h-5 text-green-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 13l4 4L19 7" />
</svg>
</button>
</div>
</div>
<div class="pb-4 font-bold dark:text-white"> {{ session('token') }}</div>
@endif
<h3 class="py-4">Issued Tokens</h3>
<div class="grid gap-2 lg:grid-cols-1">
@forelse ($tokens as $token)
<div wire:key="token-{{ $token->id }}"
class="flex flex-col gap-1 p-2 border dark:border-coolgray-200 hover:no-underline">
<div>Description: {{ $token->name }}</div>
<div>Last used: {{ $token->last_used_at ? $token->last_used_at->diffForHumans() : 'Never' }}</div>
<div class="flex gap-1">
@if ($token->abilities)
Permissions:
@foreach ($token->abilities as $ability)
<div class="font-bold dark:text-white">{{ $ability }}</div>
@endforeach
<div x-data="{ search: '' }">
<div class="flex items-center justify-between py-4">
<h3>Issued Tokens</h3>
@if ($tokens->count() > 1)
<input type="text" x-model="search" placeholder="Filter tokens..."
class="input w-64" />
@endif
</div>
<div class="flex flex-col gap-2">
@forelse ($tokens as $token)
<div wire:key="token-{{ $token->id }}"
x-show="!search || '{{ strtolower($token->name) }} {{ strtolower(implode(' ', $token->abilities ?? [])) }}'.includes(search.toLowerCase())"
class="flex items-center justify-between p-3 border rounded dark:border-coolgray-200">
<div>
<div class="flex items-center gap-2">
<span class="font-bold dark:text-white">{{ $token->name }}</span>
@if ($token->abilities)
@foreach ($token->abilities as $ability)
<span
class="px-2 py-0.5 text-xs rounded-sm font-medium {{ $ability === 'root' ? 'bg-red-500/20 text-red-400' : ($ability === 'write' || $ability === 'write:sensitive' ? 'bg-amber-500/20 text-amber-400' : ($ability === 'deploy' ? 'bg-blue-500/20 text-blue-400' : 'bg-neutral-500/20 text-neutral-300')) }}">
{{ $ability }}
</span>
@endforeach
@endif
</div>
<div class="text-xs text-neutral-400">
Last used: {{ $token->last_used_at ? $token->last_used_at->diffForHumans() : 'Never' }}
</div>
</div>
@if (auth()->id() === $token->tokenable_id)
<x-modal-confirmation title="Confirm API Token Revocation?" isErrorButton
buttonTitle="Revoke" submitAction="revoke({{ data_get($token, 'id') }})"
:actions="[
'This API Token will be revoked and permanently deleted.',
'Any API call made with this token will fail.',
]" confirmationText="{{ $token->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the API Token Description below"
shortConfirmationLabel="API Token Description" :confirmWithPassword="false"
step2ButtonText="Revoke API Token" />
@endif
</div>
@if (auth()->id() === $token->tokenable_id)
<x-modal-confirmation title="Confirm API Token Revocation?" isErrorButton buttonTitle="Revoke token"
submitAction="revoke({{ data_get($token, 'id') }})" :actions="[
'This API Token will be revoked and permanently deleted.',
'Any API call made with this token will fail.',
]"
confirmationText="{{ $token->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the API Token Description below"
shortConfirmationLabel="API Token Description" :confirmWithPassword="false"
step2ButtonText="Revoke API Token" />
@endif
</div>
@empty
<div>
<div>No API tokens found.</div>
</div>
@endforelse
@empty
<div class="text-neutral-400">No API tokens found.</div>
@endforelse
</div>
</div>
@endif
</div>