Refactor subuser permissions (#1961)

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
This commit is contained in:
Boy132 2025-12-11 14:34:27 +01:00 committed by GitHub
parent 1ab4ddb07c
commit 760aaf9bfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
112 changed files with 610 additions and 668 deletions

View File

@ -2,12 +2,13 @@
namespace App\Contracts\Http; namespace App\Contracts\Http;
use App\Enums\SubuserPermission;
interface ClientPermissionsRequest interface ClientPermissionsRequest
{ {
/** /**
* Returns the permissions string indicating which permission should be used to * Returns the permission used to validate that the authenticated user may perform
* validate that the authenticated user has permission to perform this action against * this action against the given resource (server).
* the given resource (server).
*/ */
public function permission(): string; public function permission(): SubuserPermission|string;
} }

View File

@ -0,0 +1,88 @@
<?php
namespace App\Enums;
enum SubuserPermission: string
{
case WebsocketConnect = 'websocket.connect';
case ControlConsole = 'control.console';
case ControlStart = 'control.start';
case ControlStop = 'control.stop';
case ControlRestart = 'control.restart';
case FileRead = 'file.read';
case FileReadContent = 'file.read-content';
case FileCreate = 'file.create';
case FileUpdate = 'file.update';
case FileDelete = 'file.delete';
case FileArchive = 'file.archive';
case FileSftp = 'file.sftp';
case BackupRead = 'backup.read';
case BackupCreate = 'backup.create';
case BackupDelete = 'backup.delete';
case BackupDownload = 'backup.download';
case BackupRestore = 'backup.restore';
case ScheduleRead = 'schedule.read';
case ScheduleCreate = 'schedule.create';
case ScheduleUpdate = 'schedule.update';
case ScheduleDelete = 'schedule.delete';
case UserRead = 'user.read';
case UserCreate = 'user.create';
case UserUpdate = 'user.update';
case UserDelete = 'user.delete';
case DatabaseRead = 'database.read';
case DatabaseCreate = 'database.create';
case DatabaseUpdate = 'database.update';
case DatabaseDelete = 'database.delete';
case DatabaseViewPassword = 'database.view-password';
case AllocationRead = 'allocation.read';
case AllocationCreate = 'allocation.create';
case AllocationUpdate = 'allocation.update';
case AllocationDelete = 'allocation.delete';
case ActivityRead = 'activity.read';
case StartupRead = 'startup.read';
case StartupUpdate = 'startup.update';
case StartupDockerImage = 'startup.docker-image';
case SettingsRename = 'settings.rename';
case SettingsDescription = 'settings.description';
case SettingsReinstall = 'settings.reinstall';
/** @return string[] */
public function split(): array
{
return explode('.', $this->value, 2);
}
public function isHidden(): bool
{
return $this === self::WebsocketConnect;
}
public function getIcon(): ?string
{
[$group, $permission] = $this->split();
return match ($group) {
'control' => 'tabler-terminal-2',
'user' => 'tabler-users',
'file' => 'tabler-files',
'backup' => 'tabler-file-zip',
'allocation' => 'tabler-network',
'startup' => 'tabler-player-play',
'database' => 'tabler-database',
'schedule' => 'tabler-clock',
'settings' => 'tabler-settings',
'activity' => 'tabler-stack',
default => null,
};
}
}

View File

@ -2,9 +2,9 @@
namespace App\Extensions\Features\Schemas; namespace App\Extensions\Features\Schemas;
use App\Enums\SubuserPermission;
use App\Extensions\Features\FeatureSchemaInterface; use App\Extensions\Features\FeatureSchemaInterface;
use App\Facades\Activity; use App\Facades\Activity;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\ServerVariable; use App\Models\ServerVariable;
use App\Repositories\Daemon\DaemonServerRepository; use App\Repositories\Daemon\DaemonServerRepository;
@ -54,7 +54,7 @@ class GSLTokenSchema implements FeatureSchemaInterface
->modalHeading('Invalid GSL token') ->modalHeading('Invalid GSL token')
->modalDescription('It seems like your Gameserver Login Token (GSL token) is invalid or has expired.') ->modalDescription('It seems like your Gameserver Login Token (GSL token) is invalid or has expired.')
->modalSubmitActionLabel('Update GSL Token') ->modalSubmitActionLabel('Update GSL Token')
->disabledSchema(fn () => !user()?->can(Permission::ACTION_STARTUP_UPDATE, $server)) ->disabledSchema(fn () => !user()?->can(SubuserPermission::StartupUpdate, $server))
->schema([ ->schema([
TextEntry::make('info') TextEntry::make('info')
->label(new HtmlString(Blade::render('You can either <x-filament::link href="https://steamcommunity.com/dev/managegameservers" target="_blank">generate a new one</x-filament::link> and enter it below or leave the field blank to remove it completely.'))), ->label(new HtmlString(Blade::render('You can either <x-filament::link href="https://steamcommunity.com/dev/managegameservers" target="_blank">generate a new one</x-filament::link> and enter it below or leave the field blank to remove it completely.'))),

View File

@ -2,9 +2,9 @@
namespace App\Extensions\Features\Schemas; namespace App\Extensions\Features\Schemas;
use App\Enums\SubuserPermission;
use App\Extensions\Features\FeatureSchemaInterface; use App\Extensions\Features\FeatureSchemaInterface;
use App\Facades\Activity; use App\Facades\Activity;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Repositories\Daemon\DaemonServerRepository; use App\Repositories\Daemon\DaemonServerRepository;
use Exception; use Exception;
@ -44,7 +44,7 @@ class JavaVersionSchema implements FeatureSchemaInterface
->modalHeading('Unsupported Java Version') ->modalHeading('Unsupported Java Version')
->modalDescription('This server is currently running an unsupported version of Java and cannot be started.') ->modalDescription('This server is currently running an unsupported version of Java and cannot be started.')
->modalSubmitActionLabel('Update Docker Image') ->modalSubmitActionLabel('Update Docker Image')
->disabledSchema(fn () => !user()?->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server)) ->disabledSchema(fn () => !user()?->can(SubuserPermission::StartupDockerImage, $server))
->schema([ ->schema([
TextEntry::make('java') TextEntry::make('java')
->label('Please select a supported version from the list below to continue starting the server.'), ->label('Please select a supported version from the list below to continue starting the server.'),

View File

@ -4,11 +4,11 @@ namespace App\Filament\App\Resources\Servers\Pages;
use App\Enums\CustomizationKey; use App\Enums\CustomizationKey;
use App\Enums\ServerResourceType; use App\Enums\ServerResourceType;
use App\Enums\SubuserPermission;
use App\Filament\App\Resources\Servers\ServerResource; use App\Filament\App\Resources\Servers\ServerResource;
use App\Filament\Components\Tables\Columns\ProgressBarColumn; use App\Filament\Components\Tables\Columns\ProgressBarColumn;
use App\Filament\Components\Tables\Columns\ServerEntryColumn; use App\Filament\Components\Tables\Columns\ServerEntryColumn;
use App\Filament\Server\Pages\Console; use App\Filament\Server\Pages\Console;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Repositories\Daemon\DaemonServerRepository; use App\Repositories\Daemon\DaemonServerRepository;
use App\Traits\Filament\CanCustomizeHeaderActions; use App\Traits\Filament\CanCustomizeHeaderActions;
@ -244,21 +244,21 @@ class ListServers extends ListRecords
->label(trans('server/console.power_actions.start')) ->label(trans('server/console.power_actions.start'))
->color('primary') ->color('primary')
->icon('tabler-player-play-filled') ->icon('tabler-player-play-filled')
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_START, $server)) ->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStart, $server))
->visible(fn (Server $server) => $server->retrieveStatus()->isStartable()) ->visible(fn (Server $server) => $server->retrieveStatus()->isStartable())
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'start']), ->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'start']),
Action::make('restart') Action::make('restart')
->label(trans('server/console.power_actions.restart')) ->label(trans('server/console.power_actions.restart'))
->color('gray') ->color('gray')
->icon('tabler-reload') ->icon('tabler-reload')
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_RESTART, $server)) ->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlRestart, $server))
->visible(fn (Server $server) => $server->retrieveStatus()->isRestartable()) ->visible(fn (Server $server) => $server->retrieveStatus()->isRestartable())
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'restart']), ->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'restart']),
Action::make('stop') Action::make('stop')
->label(trans('server/console.power_actions.stop')) ->label(trans('server/console.power_actions.stop'))
->color('danger') ->color('danger')
->icon('tabler-player-stop-filled') ->icon('tabler-player-stop-filled')
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server)) ->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStop, $server))
->visible(fn (Server $server) => $server->retrieveStatus()->isStoppable() && !$server->retrieveStatus()->isKillable()) ->visible(fn (Server $server) => $server->retrieveStatus()->isStoppable() && !$server->retrieveStatus()->isKillable())
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'stop']), ->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'stop']),
Action::make('kill') Action::make('kill')
@ -266,7 +266,7 @@ class ListServers extends ListRecords
->color('danger') ->color('danger')
->icon('tabler-alert-square') ->icon('tabler-alert-square')
->tooltip(trans('server/console.power_actions.kill_tooltip')) ->tooltip(trans('server/console.power_actions.kill_tooltip'))
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server)) ->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStop, $server))
->visible(fn (Server $server) => $server->retrieveStatus()->isKillable()) ->visible(fn (Server $server) => $server->retrieveStatus()->isKillable())
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'kill']), ->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'kill']),
]) ])

View File

@ -2,7 +2,7 @@
namespace App\Filament\Components\Actions; namespace App\Filament\Components\Actions;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\Schedule; use App\Models\Schedule;
use App\Models\Server; use App\Models\Server;
use App\Services\Schedules\Sharing\ScheduleExporterService; use App\Services\Schedules\Sharing\ScheduleExporterService;
@ -36,7 +36,7 @@ class ExportScheduleAction extends Action
$this->label(trans('filament-actions::export.modal.actions.export.label')); $this->label(trans('filament-actions::export.modal.actions.export.label'));
$this->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_READ, $server)); $this->authorize(fn () => user()?->can(SubuserPermission::ScheduleRead, $server));
$this->action(fn (ScheduleExporterService $service, Schedule $schedule) => response()->streamDownload(function () use ($service, $schedule) { $this->action(fn (ScheduleExporterService $service, Schedule $schedule) => response()->streamDownload(function () use ($service, $schedule) {
echo $service->handle($schedule); echo $service->handle($schedule);

View File

@ -2,7 +2,7 @@
namespace App\Filament\Components\Actions; namespace App\Filament\Components\Actions;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\Server; use App\Models\Server;
use App\Services\Schedules\Sharing\ScheduleImporterService; use App\Services\Schedules\Sharing\ScheduleImporterService;
use Exception; use Exception;
@ -33,7 +33,7 @@ class ImportScheduleAction extends Action
$this->label(trans('filament-actions::import.modal.actions.import.label')); $this->label(trans('filament-actions::import.modal.actions.import.label'));
$this->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_CREATE, $server)); $this->authorize(fn () => user()?->can(SubuserPermission::ScheduleCreate, $server));
$this->schema([ $this->schema([
Tabs::make('Tabs') Tabs::make('Tabs')

View File

@ -4,6 +4,7 @@ namespace App\Filament\Server\Pages;
use App\Enums\ConsoleWidgetPosition; use App\Enums\ConsoleWidgetPosition;
use App\Enums\ContainerStatus; use App\Enums\ContainerStatus;
use App\Enums\SubuserPermission;
use App\Exceptions\Http\Server\ServerStateConflictException; use App\Exceptions\Http\Server\ServerStateConflictException;
use App\Extensions\Features\FeatureService; use App\Extensions\Features\FeatureService;
use App\Filament\Server\Widgets\ServerConsole; use App\Filament\Server\Widgets\ServerConsole;
@ -12,7 +13,6 @@ use App\Filament\Server\Widgets\ServerMemoryChart;
use App\Filament\Server\Widgets\ServerNetworkChart; use App\Filament\Server\Widgets\ServerNetworkChart;
use App\Filament\Server\Widgets\ServerOverview; use App\Filament\Server\Widgets\ServerOverview;
use App\Livewire\AlertBanner; use App\Livewire\AlertBanner;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Traits\Filament\CanCustomizeHeaderActions; use App\Traits\Filament\CanCustomizeHeaderActions;
use Filament\Actions\Action; use Filament\Actions\Action;
@ -164,7 +164,7 @@ class Console extends Page
->label(trans('server/console.power_actions.start')) ->label(trans('server/console.power_actions.start'))
->color('primary') ->color('primary')
->icon('tabler-player-play-filled') ->icon('tabler-player-play-filled')
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_START, $server)) ->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStart, $server))
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isStartable()) ->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isStartable())
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'start')) ->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'start'))
->size(Size::ExtraLarge), ->size(Size::ExtraLarge),
@ -172,7 +172,7 @@ class Console extends Page
->label(trans('server/console.power_actions.restart')) ->label(trans('server/console.power_actions.restart'))
->color('gray') ->color('gray')
->icon('tabler-reload') ->icon('tabler-reload')
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_RESTART, $server)) ->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlRestart, $server))
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isRestartable()) ->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isRestartable())
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'restart')) ->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'restart'))
->size(Size::ExtraLarge), ->size(Size::ExtraLarge),
@ -180,7 +180,7 @@ class Console extends Page
->label(trans('server/console.power_actions.stop')) ->label(trans('server/console.power_actions.stop'))
->color('danger') ->color('danger')
->icon('tabler-player-stop-filled') ->icon('tabler-player-stop-filled')
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server)) ->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStop, $server))
->visible(fn () => !$this->status->isKillable()) ->visible(fn () => !$this->status->isKillable())
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isStoppable()) ->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isStoppable())
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'stop')) ->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'stop'))
@ -191,7 +191,7 @@ class Console extends Page
->icon('tabler-alert-square') ->icon('tabler-alert-square')
->tooltip(trans('server/console.power_actions.kill_tooltip')) ->tooltip(trans('server/console.power_actions.kill_tooltip'))
->requiresConfirmation() ->requiresConfirmation()
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server)) ->authorize(fn (Server $server) => user()?->can(SubuserPermission::ControlStop, $server))
->visible(fn () => $this->status->isKillable()) ->visible(fn () => $this->status->isKillable())
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isKillable()) ->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isKillable())
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'kill')) ->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'kill'))

View File

@ -2,8 +2,8 @@
namespace App\Filament\Server\Pages; namespace App\Filament\Server\Pages;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Services\Servers\ReinstallServerService; use App\Services\Servers\ReinstallServerService;
use Exception; use Exception;
@ -60,7 +60,7 @@ class Settings extends ServerFormPage
->columnStart(1) ->columnStart(1)
->columnSpanFull() ->columnSpanFull()
->label(trans('server/setting.server_info.name')) ->label(trans('server/setting.server_info.name'))
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_RENAME, $server)) ->disabled(fn (Server $server) => !user()?->can(SubuserPermission::SettingsRename, $server))
->required() ->required()
->live(onBlur: true) ->live(onBlur: true)
->afterStateUpdated(fn ($state, Server $server) => $this->updateName($state, $server)), ->afterStateUpdated(fn ($state, Server $server) => $this->updateName($state, $server)),
@ -69,7 +69,7 @@ class Settings extends ServerFormPage
->columnSpanFull() ->columnSpanFull()
->label(trans('server/setting.server_info.description')) ->label(trans('server/setting.server_info.description'))
->hidden(!config('panel.editable_server_descriptions')) ->hidden(!config('panel.editable_server_descriptions'))
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_DESCRIPTION, $server)) ->disabled(fn (Server $server) => !user()?->can(SubuserPermission::SettingsDescription, $server))
->autosize() ->autosize()
->live(onBlur: true) ->live(onBlur: true)
->afterStateUpdated(fn ($state, Server $server) => $this->updateDescription($state ?? '', $server)), ->afterStateUpdated(fn ($state, Server $server) => $this->updateDescription($state ?? '', $server)),
@ -319,7 +319,7 @@ class Settings extends ServerFormPage
]), ]),
Fieldset::make(trans('server/setting.server_info.sftp.title')) Fieldset::make(trans('server/setting.server_info.sftp.title'))
->columnSpanFull() ->columnSpanFull()
->hidden(fn (Server $server) => !user()?->can(Permission::ACTION_FILE_SFTP, $server)) ->hidden(fn (Server $server) => !user()?->can(SubuserPermission::FileSftp, $server))
->columns([ ->columns([
'default' => 1, 'default' => 1,
'sm' => 1, 'sm' => 1,
@ -361,19 +361,19 @@ class Settings extends ServerFormPage
]), ]),
]), ]),
Section::make(trans('server/setting.reinstall.title')) Section::make(trans('server/setting.reinstall.title'))
->hidden(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server)) ->hidden(fn (Server $server) => !user()?->can(SubuserPermission::SettingsReinstall, $server))
->columnSpanFull() ->columnSpanFull()
->footerActions([ ->footerActions([
Action::make('reinstall') Action::make('reinstall')
->label(trans('server/setting.reinstall.action')) ->label(trans('server/setting.reinstall.action'))
->color('danger') ->color('danger')
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server)) ->disabled(fn (Server $server) => !user()?->can(SubuserPermission::SettingsReinstall, $server))
->requiresConfirmation() ->requiresConfirmation()
->modalHeading(trans('server/setting.reinstall.modal')) ->modalHeading(trans('server/setting.reinstall.modal'))
->modalDescription(trans('server/setting.reinstall.modal_description')) ->modalDescription(trans('server/setting.reinstall.modal_description'))
->modalSubmitActionLabel(trans('server/setting.reinstall.yes')) ->modalSubmitActionLabel(trans('server/setting.reinstall.yes'))
->action(function (Server $server, ReinstallServerService $reinstallService) { ->action(function (Server $server, ReinstallServerService $reinstallService) {
abort_unless(user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server), 403); abort_unless(user()?->can(SubuserPermission::SettingsReinstall, $server), 403);
try { try {
$reinstallService->handle($server); $reinstallService->handle($server);
@ -412,7 +412,7 @@ class Settings extends ServerFormPage
public function updateName(string $name, Server $server): void public function updateName(string $name, Server $server): void
{ {
abort_unless(user()?->can(Permission::ACTION_SETTINGS_RENAME, $server), 403); abort_unless(user()?->can(SubuserPermission::SettingsRename, $server), 403);
$original = $server->name; $original = $server->name;
@ -443,7 +443,7 @@ class Settings extends ServerFormPage
public function updateDescription(string $description, Server $server): void public function updateDescription(string $description, Server $server): void
{ {
abort_unless(user()?->can(Permission::ACTION_SETTINGS_DESCRIPTION, $server) && config('panel.editable_server_descriptions'), 403); abort_unless(user()?->can(SubuserPermission::SettingsDescription, $server) && config('panel.editable_server_descriptions'), 403);
$original = $server->description; $original = $server->description;

View File

@ -2,10 +2,10 @@
namespace App\Filament\Server\Pages; namespace App\Filament\Server\Pages;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Components\Actions\PreviewStartupAction; use App\Filament\Components\Actions\PreviewStartupAction;
use App\Filament\Components\Forms\Fields\StartupVariable; use App\Filament\Components\Forms\Fields\StartupVariable;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\ServerVariable; use App\Models\ServerVariable;
use Exception; use Exception;
@ -51,7 +51,7 @@ class Startup extends ServerFormPage
->label(trans('server/startup.command')) ->label(trans('server/startup.command'))
->live() ->live()
->visible(fn (Server $server) => in_array($server->startup, $server->egg->startup_commands)) ->visible(fn (Server $server) => in_array($server->startup, $server->egg->startup_commands))
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_STARTUP_UPDATE, $server)) ->disabled(fn (Server $server) => !user()?->can(SubuserPermission::StartupUpdate, $server))
->formatStateUsing(fn (Server $server) => $server->startup) ->formatStateUsing(fn (Server $server) => $server->startup)
->afterStateUpdated(function ($state, Server $server, Set $set) { ->afterStateUpdated(function ($state, Server $server, Set $set) {
$original = $server->startup; $original = $server->startup;
@ -85,7 +85,7 @@ class Startup extends ServerFormPage
->label(trans('server/startup.docker_image')) ->label(trans('server/startup.docker_image'))
->live() ->live()
->visible(fn (Server $server) => in_array($server->image, $server->egg->docker_images)) ->visible(fn (Server $server) => in_array($server->image, $server->egg->docker_images))
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server)) ->disabled(fn (Server $server) => !user()?->can(SubuserPermission::StartupDockerImage, $server))
->afterStateUpdated(function ($state, Server $server) { ->afterStateUpdated(function ($state, Server $server) {
$original = $server->image; $original = $server->image;
$server->forceFill(['image' => $state])->saveOrFail(); $server->forceFill(['image' => $state])->saveOrFail();
@ -123,7 +123,7 @@ class Startup extends ServerFormPage
return $query->where('egg_variables.user_viewable', true)->orderByPowerJoins('variable.sort'); return $query->where('egg_variables.user_viewable', true)->orderByPowerJoins('variable.sort');
}) })
->grid() ->grid()
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_STARTUP_UPDATE, $server)) ->disabled(fn (Server $server) => !user()?->can(SubuserPermission::StartupUpdate, $server))
->reorderable(false)->addable(false)->deletable(false) ->reorderable(false)->addable(false)->deletable(false)
->schema([ ->schema([
StartupVariable::make('variable_value') StartupVariable::make('variable_value')
@ -139,12 +139,12 @@ class Startup extends ServerFormPage
protected function authorizeAccess(): void protected function authorizeAccess(): void
{ {
abort_unless(user()?->can(Permission::ACTION_STARTUP_READ, Filament::getTenant()), 403); abort_unless(user()?->can(SubuserPermission::StartupRead, Filament::getTenant()), 403);
} }
public static function canAccess(): bool public static function canAccess(): bool
{ {
return parent::canAccess() && user()?->can(Permission::ACTION_STARTUP_READ, Filament::getTenant()); return parent::canAccess() && user()?->can(SubuserPermission::StartupRead, Filament::getTenant());
} }
public function update(?string $state, ServerVariable $serverVariable): null public function update(?string $state, ServerVariable $serverVariable): null

View File

@ -2,10 +2,10 @@
namespace App\Filament\Server\Resources\Allocations; namespace App\Filament\Server\Resources\Allocations;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Server\Resources\Allocations\Pages\ListAllocations; use App\Filament\Server\Resources\Allocations\Pages\ListAllocations;
use App\Models\Allocation; use App\Models\Allocation;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Services\Allocations\FindAssignableAllocationService; use App\Services\Allocations\FindAssignableAllocationService;
use App\Traits\Filament\BlockAccessInConflict; use App\Traits\Filament\BlockAccessInConflict;
@ -57,7 +57,7 @@ class AllocationResource extends Resource
TextInputColumn::make('notes') TextInputColumn::make('notes')
->label(trans('server/network.notes')) ->label(trans('server/network.notes'))
->visibleFrom('sm') ->visibleFrom('sm')
->disabled(fn () => !user()?->can(Permission::ACTION_ALLOCATION_UPDATE, $server)) ->disabled(fn () => !user()?->can(SubuserPermission::AllocationUpdate, $server))
->placeholder(trans('server/network.no_notes')), ->placeholder(trans('server/network.no_notes')),
IconColumn::make('primary') IconColumn::make('primary')
->icon(fn ($state) => match ($state) { ->icon(fn ($state) => match ($state) {
@ -69,7 +69,7 @@ class AllocationResource extends Resource
default => 'gray', default => 'gray',
}) })
->tooltip(fn (Allocation $allocation) => $allocation->id === $server->allocation_id ? trans('server/network.primary') : trans('server/network.make_primary')) ->tooltip(fn (Allocation $allocation) => $allocation->id === $server->allocation_id ? trans('server/network.primary') : trans('server/network.make_primary'))
->action(fn (Allocation $allocation) => user()?->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id])) ->action(fn (Allocation $allocation) => user()?->can(SubuserPermission::AllocationUpdate, $server) && $server->update(['allocation_id' => $allocation->id]))
->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id) ->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
->label(trans('server/network.primary')), ->label(trans('server/network.primary')),
IconColumn::make('is_locked') IconColumn::make('is_locked')
@ -81,7 +81,7 @@ class AllocationResource extends Resource
->recordActions([ ->recordActions([
DetachAction::make() DetachAction::make()
->visible(fn (Allocation $allocation) => !$allocation->is_locked || user()?->can('update', $allocation->node)) ->visible(fn (Allocation $allocation) => !$allocation->is_locked || user()?->can('update', $allocation->node))
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_DELETE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::AllocationDelete, $server))
->label(trans('server/network.delete')) ->label(trans('server/network.delete'))
->action(function (Allocation $allocation) { ->action(function (Allocation $allocation) {
Allocation::where('id', $allocation->id)->update([ Allocation::where('id', $allocation->id)->update([
@ -101,7 +101,7 @@ class AllocationResource extends Resource
Action::make('add_allocation') Action::make('add_allocation')
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network') ->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network')
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_CREATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::AllocationCreate, $server))
->tooltip(fn () => $server->allocations()->count() >= $server->allocation_limit ? trans('server/network.limit') : trans('server/network.add')) ->tooltip(fn () => $server->allocations()->count() >= $server->allocation_limit ? trans('server/network.limit') : trans('server/network.add'))
->hidden(fn () => !config('panel.client_features.allocations.enabled') || $server->allocation === null) ->hidden(fn () => !config('panel.client_features.allocations.enabled') || $server->allocation === null)
->disabled(fn () => $server->allocations()->count() >= $server->allocation_limit) ->disabled(fn () => $server->allocations()->count() >= $server->allocation_limit)

View File

@ -4,13 +4,13 @@ namespace App\Filament\Server\Resources\Backups;
use App\Enums\BackupStatus; use App\Enums\BackupStatus;
use App\Enums\ServerState; use App\Enums\ServerState;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Components\Tables\Columns\BytesColumn; use App\Filament\Components\Tables\Columns\BytesColumn;
use App\Filament\Components\Tables\Columns\DateTimeColumn; use App\Filament\Components\Tables\Columns\DateTimeColumn;
use App\Filament\Server\Resources\Backups\Pages\ListBackups; use App\Filament\Server\Resources\Backups\Pages\ListBackups;
use App\Http\Controllers\Api\Client\Servers\BackupController; use App\Http\Controllers\Api\Client\Servers\BackupController;
use App\Models\Backup; use App\Models\Backup;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Repositories\Daemon\DaemonBackupRepository; use App\Repositories\Daemon\DaemonBackupRepository;
use App\Services\Backups\DeleteBackupService; use App\Services\Backups\DeleteBackupService;
@ -128,7 +128,7 @@ class BackupResource extends Resource
ActionGroup::make([ ActionGroup::make([
Action::make('rename') Action::make('rename')
->icon('tabler-pencil') ->icon('tabler-pencil')
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DELETE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::BackupDelete, $server))
->label(trans('server/backup.actions.rename.title')) ->label(trans('server/backup.actions.rename.title'))
->schema([ ->schema([
TextInput::make('name') TextInput::make('name')
@ -159,7 +159,7 @@ class BackupResource extends Resource
Action::make('lock') Action::make('lock')
->iconSize(IconSize::Large) ->iconSize(IconSize::Large)
->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open') ->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open')
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DELETE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::BackupDelete, $server))
->label(fn (Backup $backup) => !$backup->is_locked ? trans('server/backup.actions.lock.lock') : trans('server/backup.actions.lock.unlock')) ->label(fn (Backup $backup) => !$backup->is_locked ? trans('server/backup.actions.lock.lock') : trans('server/backup.actions.lock.unlock'))
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup)) ->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup))
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful), ->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
@ -168,7 +168,7 @@ class BackupResource extends Resource
->iconSize(IconSize::Large) ->iconSize(IconSize::Large)
->color('primary') ->color('primary')
->icon('tabler-download') ->icon('tabler-download')
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) ->authorize(fn () => user()?->can(SubuserPermission::BackupDownload, $server))
->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true) ->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true)
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful), ->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
Action::make('restore') Action::make('restore')
@ -176,7 +176,7 @@ class BackupResource extends Resource
->iconSize(IconSize::Large) ->iconSize(IconSize::Large)
->color('success') ->color('success')
->icon('tabler-folder-up') ->icon('tabler-folder-up')
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_RESTORE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::BackupRestore, $server))
->schema([ ->schema([
TextEntry::make('stop_info') TextEntry::make('stop_info')
->hiddenLabel() ->hiddenLabel()
@ -258,7 +258,7 @@ class BackupResource extends Resource
]) ])
->toolbarActions([ ->toolbarActions([
CreateAction::make() CreateAction::make()
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_CREATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::BackupCreate, $server))
->icon('tabler-file-zip') ->icon('tabler-file-zip')
->tooltip(fn () => $server->backups()->count() >= $server->backup_limit ? trans('server/backup.actions.create.limit') : trans('server/backup.actions.create.title')) ->tooltip(fn () => $server->backups()->count() >= $server->backup_limit ? trans('server/backup.actions.create.limit') : trans('server/backup.actions.create.title'))
->disabled(fn () => $server->backups()->count() >= $server->backup_limit) ->disabled(fn () => $server->backups()->count() >= $server->backup_limit)
@ -269,7 +269,7 @@ class BackupResource extends Resource
->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) { ->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) {
$action = $initiateBackupService->setIgnoredFiles(explode(PHP_EOL, $data['ignored'] ?? '')); $action = $initiateBackupService->setIgnoredFiles(explode(PHP_EOL, $data['ignored'] ?? ''));
if (user()?->can(Permission::ACTION_BACKUP_DELETE, $server)) { if (user()?->can(SubuserPermission::BackupDelete, $server)) {
$action->setIsLocked((bool) $data['is_locked']); $action->setIsLocked((bool) $data['is_locked']);
} }

View File

@ -2,12 +2,12 @@
namespace App\Filament\Server\Resources\Databases; namespace App\Filament\Server\Resources\Databases;
use App\Enums\SubuserPermission;
use App\Filament\Components\Actions\RotateDatabasePasswordAction; use App\Filament\Components\Actions\RotateDatabasePasswordAction;
use App\Filament\Components\Tables\Columns\DateTimeColumn; use App\Filament\Components\Tables\Columns\DateTimeColumn;
use App\Filament\Server\Resources\Databases\Pages\ListDatabases; use App\Filament\Server\Resources\Databases\Pages\ListDatabases;
use App\Models\Database; use App\Models\Database;
use App\Models\DatabaseHost; use App\Models\DatabaseHost;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Services\Databases\DatabaseManagementService; use App\Services\Databases\DatabaseManagementService;
use App\Traits\Filament\BlockAccessInConflict; use App\Traits\Filament\BlockAccessInConflict;
@ -87,10 +87,10 @@ class DatabaseResource extends Resource
TextInput::make('password') TextInput::make('password')
->label(trans('server/database.password')) ->label(trans('server/database.password'))
->password()->revealable() ->password()->revealable()
->hidden(fn () => !user()?->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server)) ->hidden(fn () => !user()?->can(SubuserPermission::DatabaseViewPassword, $server))
->hintAction( ->hintAction(
RotateDatabasePasswordAction::make() RotateDatabasePasswordAction::make()
->authorize(fn () => user()?->can(Permission::ACTION_DATABASE_UPDATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::DatabaseUpdate, $server))
) )
->copyable() ->copyable()
->formatStateUsing(fn (Database $database) => $database->password), ->formatStateUsing(fn (Database $database) => $database->password),
@ -102,7 +102,7 @@ class DatabaseResource extends Resource
TextInput::make('jdbc') TextInput::make('jdbc')
->label(trans('server/database.jdbc')) ->label(trans('server/database.jdbc'))
->password()->revealable() ->password()->revealable()
->hidden(!user()?->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server)) ->hidden(!user()?->can(SubuserPermission::DatabaseViewPassword, $server))
->copyable() ->copyable()
->columnSpanFull() ->columnSpanFull()
->formatStateUsing(fn (Database $database) => $database->jdbc), ->formatStateUsing(fn (Database $database) => $database->jdbc),

View File

@ -2,9 +2,9 @@
namespace App\Filament\Server\Resources\Files\Pages; namespace App\Filament\Server\Resources\Files\Pages;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Server\Resources\Files\FileResource; use App\Filament\Server\Resources\Files\FileResource;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Services\Nodes\NodeJWTService; use App\Services\Nodes\NodeJWTService;
use Carbon\CarbonImmutable; use Carbon\CarbonImmutable;
@ -55,7 +55,7 @@ class DownloadFiles extends Page
protected function authorizeAccess(): void protected function authorizeAccess(): void
{ {
abort_unless(user()?->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()), 403); abort_unless(user()?->can(SubuserPermission::FileReadContent, Filament::getTenant()), 403);
} }
public static function route(string $path): PageRegistration public static function route(string $path): PageRegistration

View File

@ -2,13 +2,13 @@
namespace App\Filament\Server\Resources\Files\Pages; namespace App\Filament\Server\Resources\Files\Pages;
use App\Enums\SubuserPermission;
use App\Exceptions\Http\Server\FileSizeTooLargeException; use App\Exceptions\Http\Server\FileSizeTooLargeException;
use App\Exceptions\Repository\FileNotEditableException; use App\Exceptions\Repository\FileNotEditableException;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Server\Resources\Files\FileResource; use App\Filament\Server\Resources\Files\FileResource;
use App\Livewire\AlertBanner; use App\Livewire\AlertBanner;
use App\Models\File; use App\Models\File;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Repositories\Daemon\DaemonFileRepository; use App\Repositories\Daemon\DaemonFileRepository;
use App\Traits\Filament\CanCustomizeHeaderActions; use App\Traits\Filament\CanCustomizeHeaderActions;
@ -83,7 +83,7 @@ class EditFiles extends Page
->footerActions([ ->footerActions([
Action::make('save_and_close') Action::make('save_and_close')
->label(trans('server/file.actions.edit.save_close')) ->label(trans('server/file.actions.edit.save_close'))
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
->icon('tabler-device-floppy') ->icon('tabler-device-floppy')
->keyBindings('mod+shift+s') ->keyBindings('mod+shift+s')
->action(function () { ->action(function () {
@ -103,7 +103,7 @@ class EditFiles extends Page
}), }),
Action::make('save') Action::make('save')
->label(trans('server/file.actions.edit.save')) ->label(trans('server/file.actions.edit.save'))
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
->icon('tabler-device-floppy') ->icon('tabler-device-floppy')
->keyBindings('mod+s') ->keyBindings('mod+s')
->action(function () { ->action(function () {
@ -233,7 +233,7 @@ class EditFiles extends Page
protected function authorizeAccess(): void protected function authorizeAccess(): void
{ {
abort_unless(user()?->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()), 403); abort_unless(user()?->can(SubuserPermission::FileReadContent, Filament::getTenant()), 403);
} }
/** /**

View File

@ -2,6 +2,7 @@
namespace App\Filament\Server\Resources\Files\Pages; namespace App\Filament\Server\Resources\Files\Pages;
use App\Enums\SubuserPermission;
use App\Exceptions\Repository\FileExistsException; use App\Exceptions\Repository\FileExistsException;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Components\Tables\Columns\BytesColumn; use App\Filament\Components\Tables\Columns\BytesColumn;
@ -9,7 +10,6 @@ use App\Filament\Components\Tables\Columns\DateTimeColumn;
use App\Filament\Server\Resources\Files\FileResource; use App\Filament\Server\Resources\Files\FileResource;
use App\Livewire\AlertBanner; use App\Livewire\AlertBanner;
use App\Models\File; use App\Models\File;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Repositories\Daemon\DaemonFileRepository; use App\Repositories\Daemon\DaemonFileRepository;
use App\Services\Nodes\NodeJWTService; use App\Services\Nodes\NodeJWTService;
@ -122,7 +122,7 @@ class ListFiles extends ListRecords
return self::getUrl(['path' => encode_path(join_paths($this->path, $file->name))]); return self::getUrl(['path' => encode_path(join_paths($this->path, $file->name))]);
} }
if (!user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server)) { if (!user()?->can(SubuserPermission::FileReadContent, $server)) {
return null; return null;
} }
@ -130,18 +130,18 @@ class ListFiles extends ListRecords
}) })
->recordActions([ ->recordActions([
Action::make('view') Action::make('view')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileRead, $server))
->label(trans('server/file.actions.open')) ->label(trans('server/file.actions.open'))
->icon('tabler-eye')->iconSize(IconSize::Large) ->icon('tabler-eye')->iconSize(IconSize::Large)
->visible(fn (File $file) => $file->is_directory) ->visible(fn (File $file) => $file->is_directory)
->url(fn (File $file) => self::getUrl(['path' => encode_path(join_paths($this->path, $file->name))])), ->url(fn (File $file) => self::getUrl(['path' => encode_path(join_paths($this->path, $file->name))])),
EditAction::make('edit') EditAction::make('edit')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileReadContent, $server))
->visible(fn (File $file) => $file->canEdit()) ->visible(fn (File $file) => $file->canEdit())
->url(fn (File $file) => EditFiles::getUrl(['path' => encode_path(join_paths($this->path, $file->name))])), ->url(fn (File $file) => EditFiles::getUrl(['path' => encode_path(join_paths($this->path, $file->name))])),
ActionGroup::make([ ActionGroup::make([
Action::make('rename') Action::make('rename')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
->label(trans('server/file.actions.rename.title')) ->label(trans('server/file.actions.rename.title'))
->icon('tabler-forms')->iconSize(IconSize::Large) ->icon('tabler-forms')->iconSize(IconSize::Large)
->schema([ ->schema([
@ -171,7 +171,7 @@ class ListFiles extends ListRecords
$this->refreshPage(); $this->refreshPage();
}), }),
Action::make('copy') Action::make('copy')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
->label(trans('server/file.actions.copy.title')) ->label(trans('server/file.actions.copy.title'))
->icon('tabler-copy')->iconSize(IconSize::Large) ->icon('tabler-copy')->iconSize(IconSize::Large)
->visible(fn (File $file) => $file->is_file) ->visible(fn (File $file) => $file->is_file)
@ -190,13 +190,13 @@ class ListFiles extends ListRecords
$this->refreshPage(); $this->refreshPage();
}), }),
Action::make('download') Action::make('download')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileReadContent, $server))
->label(trans('server/file.actions.download')) ->label(trans('server/file.actions.download'))
->icon('tabler-download')->iconSize(IconSize::Large) ->icon('tabler-download')->iconSize(IconSize::Large)
->visible(fn (File $file) => $file->is_file) ->visible(fn (File $file) => $file->is_file)
->url(fn (File $file) => DownloadFiles::getUrl(['path' => encode_path(join_paths($this->path, $file->name))]), true), ->url(fn (File $file) => DownloadFiles::getUrl(['path' => encode_path(join_paths($this->path, $file->name))]), true),
Action::make('move') Action::make('move')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
->label(trans('server/file.actions.move.title')) ->label(trans('server/file.actions.move.title'))
->icon('tabler-replace')->iconSize(IconSize::Large) ->icon('tabler-replace')->iconSize(IconSize::Large)
->schema([ ->schema([
@ -233,7 +233,7 @@ class ListFiles extends ListRecords
$this->refreshPage(); $this->refreshPage();
}), }),
Action::make('permissions') Action::make('permissions')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
->label(trans('server/file.actions.permissions.title')) ->label(trans('server/file.actions.permissions.title'))
->icon('tabler-license')->iconSize(IconSize::Large) ->icon('tabler-license')->iconSize(IconSize::Large)
->schema([ ->schema([
@ -295,7 +295,7 @@ class ListFiles extends ListRecords
->send(); ->send();
}), }),
Action::make('archive') Action::make('archive')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileArchive, $server))
->label(trans('server/file.actions.archive.title')) ->label(trans('server/file.actions.archive.title'))
->icon('tabler-archive')->iconSize(IconSize::Large) ->icon('tabler-archive')->iconSize(IconSize::Large)
->schema([ ->schema([
@ -335,7 +335,7 @@ class ListFiles extends ListRecords
$this->refreshPage(); $this->refreshPage();
}), }),
Action::make('unarchive') Action::make('unarchive')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileArchive, $server))
->label(trans('server/file.actions.unarchive.title')) ->label(trans('server/file.actions.unarchive.title'))
->icon('tabler-archive')->iconSize(IconSize::Large) ->icon('tabler-archive')->iconSize(IconSize::Large)
->visible(fn (File $file) => $file->isArchive()) ->visible(fn (File $file) => $file->isArchive())
@ -356,7 +356,7 @@ class ListFiles extends ListRecords
}), }),
])->iconSize(IconSize::Large), ])->iconSize(IconSize::Large),
DeleteAction::make() DeleteAction::make()
->authorize(fn () => user()?->can(Permission::ACTION_FILE_DELETE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileDelete, $server))
->hiddenLabel() ->hiddenLabel()
->iconSize(IconSize::Large) ->iconSize(IconSize::Large)
->requiresConfirmation() ->requiresConfirmation()
@ -376,7 +376,7 @@ class ListFiles extends ListRecords
->toolbarActions([ ->toolbarActions([
BulkActionGroup::make([ BulkActionGroup::make([
BulkAction::make('move') BulkAction::make('move')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileUpdate, $server))
->schema([ ->schema([
TextInput::make('location') TextInput::make('location')
->label(trans('server/file.actions.move.directory')) ->label(trans('server/file.actions.move.directory'))
@ -405,7 +405,7 @@ class ListFiles extends ListRecords
$this->refreshPage(); $this->refreshPage();
}), }),
BulkAction::make('archive') BulkAction::make('archive')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileArchive, $server))
->schema([ ->schema([
Grid::make(3) Grid::make(3)
->schema([ ->schema([
@ -446,7 +446,7 @@ class ListFiles extends ListRecords
}), }),
DeleteBulkAction::make() DeleteBulkAction::make()
->successNotificationTitle(null) ->successNotificationTitle(null)
->authorize(fn () => user()?->can(Permission::ACTION_FILE_DELETE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileDelete, $server))
->action(function (Collection $files) { ->action(function (Collection $files) {
$files = $files->map(fn ($file) => $file['name'])->toArray(); $files = $files->map(fn ($file) => $file['name'])->toArray();
$this->getDaemonFileRepository()->deleteFiles($this->path, $files); $this->getDaemonFileRepository()->deleteFiles($this->path, $files);
@ -466,7 +466,7 @@ class ListFiles extends ListRecords
]), ]),
Action::make('new_file') Action::make('new_file')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
->tooltip(trans('server/file.actions.new_file.title')) ->tooltip(trans('server/file.actions.new_file.title'))
->hiddenLabel()->icon('tabler-file-plus')->iconButton()->iconSize(IconSize::ExtraLarge) ->hiddenLabel()->icon('tabler-file-plus')->iconButton()->iconSize(IconSize::ExtraLarge)
->color('primary') ->color('primary')
@ -499,7 +499,7 @@ class ListFiles extends ListRecords
->hiddenLabel(), ->hiddenLabel(),
]), ]),
Action::make('new_folder') Action::make('new_folder')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
->hiddenLabel()->icon('tabler-folder-plus')->iconButton()->iconSize(IconSize::ExtraLarge) ->hiddenLabel()->icon('tabler-folder-plus')->iconButton()->iconSize(IconSize::ExtraLarge)
->tooltip(trans('server/file.actions.new_folder.title')) ->tooltip(trans('server/file.actions.new_folder.title'))
->color('primary') ->color('primary')
@ -530,10 +530,10 @@ class ListFiles extends ListRecords
->required(), ->required(),
]), ]),
Action::make('uploadFile') Action::make('uploadFile')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
->view('filament.server.pages.file-upload'), ->view('filament.server.pages.file-upload'),
Action::make('uploadURL') Action::make('uploadURL')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileCreate, $server))
->hiddenLabel()->icon('tabler-download')->iconButton()->iconSize(IconSize::ExtraLarge) ->hiddenLabel()->icon('tabler-download')->iconButton()->iconSize(IconSize::ExtraLarge)
->tooltip(trans('server/file.actions.upload.from_url')) ->tooltip(trans('server/file.actions.upload.from_url'))
->modalHeading(trans('server/file.actions.upload.from_url')) ->modalHeading(trans('server/file.actions.upload.from_url'))
@ -555,7 +555,7 @@ class ListFiles extends ListRecords
->url(), ->url(),
]), ]),
Action::make('search') Action::make('search')
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ, $server)) ->authorize(fn () => user()?->can(SubuserPermission::FileRead, $server))
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge) ->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
->tooltip(trans('server/file.actions.nested_search.title')) ->tooltip(trans('server/file.actions.nested_search.title'))
->color('primary') ->color('primary')
@ -605,7 +605,7 @@ class ListFiles extends ListRecords
/** @var Server $server */ /** @var Server $server */
$server = Filament::getTenant(); $server = Filament::getTenant();
if (!user()?->can(Permission::ACTION_FILE_CREATE, $server)) { if (!user()?->can(SubuserPermission::FileCreate, $server)) {
abort(403, 'You do not have permission to upload files.'); abort(403, 'You do not have permission to upload files.');
} }
@ -640,7 +640,7 @@ class ListFiles extends ListRecords
/** @var Server $server */ /** @var Server $server */
$server = Filament::getTenant(); $server = Filament::getTenant();
if (!user()?->can(Permission::ACTION_FILE_CREATE, $server)) { if (!user()?->can(SubuserPermission::FileCreate, $server)) {
abort(403, 'You do not have permission to create folders.'); abort(403, 'You do not have permission to create folders.');
} }

View File

@ -3,9 +3,9 @@
namespace App\Filament\Server\Resources\Schedules\Pages; namespace App\Filament\Server\Resources\Schedules\Pages;
use App\Enums\ScheduleStatus; use App\Enums\ScheduleStatus;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Server\Resources\Schedules\ScheduleResource; use App\Filament\Server\Resources\Schedules\ScheduleResource;
use App\Models\Permission;
use App\Models\Schedule; use App\Models\Schedule;
use App\Services\Schedules\ProcessScheduleService; use App\Services\Schedules\ProcessScheduleService;
use App\Traits\Filament\CanCustomizeHeaderActions; use App\Traits\Filament\CanCustomizeHeaderActions;
@ -29,7 +29,7 @@ class ViewSchedule extends ViewRecord
{ {
return [ return [
Action::make('run_now') Action::make('run_now')
->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant())) ->authorize(fn () => user()?->can(SubuserPermission::ScheduleUpdate, Filament::getTenant()))
->label(fn (Schedule $schedule) => $schedule->tasks->count() === 0 ? trans('server/schedule.no_tasks') : ($schedule->status === ScheduleStatus::Processing ? ScheduleStatus::Processing->getLabel() : trans('server/schedule.run_now'))) ->label(fn (Schedule $schedule) => $schedule->tasks->count() === 0 ? trans('server/schedule.no_tasks') : ($schedule->status === ScheduleStatus::Processing ? ScheduleStatus::Processing->getLabel() : trans('server/schedule.run_now')))
->color(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing ? 'warning' : 'primary') ->color(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing ? 'warning' : 'primary')
->disabled(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing) ->disabled(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing)

View File

@ -2,9 +2,9 @@
namespace App\Filament\Server\Resources\Subusers; namespace App\Filament\Server\Resources\Subusers;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Filament\Server\Resources\Subusers\Pages\ListSubusers; use App\Filament\Server\Resources\Subusers\Pages\ListSubusers;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser; use App\Models\Subuser;
use App\Services\Subusers\SubuserCreationService; use App\Services\Subusers\SubuserCreationService;
@ -68,7 +68,11 @@ class SubuserResource extends Resource
$tabs = []; $tabs = [];
$permissionsArray = []; $permissionsArray = [];
foreach (Permission::permissionData() as $data) { foreach (Subuser::allPermissionData() as $data) {
if ($data['hidden']) {
continue;
}
$options = []; $options = [];
$descriptions = []; $descriptions = [];
@ -84,6 +88,7 @@ class SubuserResource extends Resource
Section::make() Section::make()
->description(trans('server/user.permissions.' . $data['name'] . '_desc')) ->description(trans('server/user.permissions.' . $data['name'] . '_desc'))
->icon($data['icon']) ->icon($data['icon'])
->contained(false)
->schema([ ->schema([
CheckboxList::make($data['name']) CheckboxList::make($data['name'])
->hiddenLabel() ->hiddenLabel()
@ -109,9 +114,12 @@ class SubuserResource extends Resource
TextColumn::make('user.email') TextColumn::make('user.email')
->label(trans('server/user.email')) ->label(trans('server/user.email'))
->searchable(), ->searchable(),
TextColumn::make('permissions') TextColumn::make('permissions_count')
->label(trans('server/user.permissions.title')) ->label(trans('server/user.permissions.title'))
->state(fn (Subuser $subuser) => count($subuser->permissions) - 1), ->state(fn (Subuser $subuser) => collect($subuser->permissions)
->reject(fn (string $permission) => SubuserPermission::tryFrom($permission)?->isHidden() ?? false)
->count()
),
]) ])
->recordActions([ ->recordActions([
DeleteAction::make() DeleteAction::make()
@ -129,14 +137,14 @@ class SubuserResource extends Resource
EditAction::make() EditAction::make()
->label(trans('server/user.edit')) ->label(trans('server/user.edit'))
->hidden(fn (Subuser $subuser) => user()?->id === $subuser->user->id) ->hidden(fn (Subuser $subuser) => user()?->id === $subuser->user->id)
->authorize(fn () => user()?->can(Permission::ACTION_USER_UPDATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::UserUpdate, $server))
->modalHeading(fn (Subuser $subuser) => trans('server/user.editing', ['user' => $subuser->user->email])) ->modalHeading(fn (Subuser $subuser) => trans('server/user.editing', ['user' => $subuser->user->email]))
->successNotificationTitle(null) ->successNotificationTitle(null)
->action(function (array $data, SubuserUpdateService $subuserUpdateService, Subuser $subuser) use ($server) { ->action(function (array $data, SubuserUpdateService $subuserUpdateService, Subuser $subuser) use ($server) {
$permissions = collect($data) $permissions = collect($data)
->forget('email') ->forget('email')
->flatMap(fn ($permissions, $key) => collect($permissions)->map(fn ($permission) => "$key.$permission")) ->flatMap(fn ($permissions, $key) => collect($permissions)->map(fn ($permission) => "$key.$permission"))
->push(Permission::ACTION_WEBSOCKET_CONNECT) ->push(SubuserPermission::WebsocketConnect->value)
->unique() ->unique()
->all(); ->all();
@ -212,7 +220,7 @@ class SubuserResource extends Resource
->icon('tabler-user-plus') ->icon('tabler-user-plus')
->tooltip(trans('server/user.invite_user')) ->tooltip(trans('server/user.invite_user'))
->createAnother(false) ->createAnother(false)
->authorize(fn () => user()?->can(Permission::ACTION_USER_CREATE, $server)) ->authorize(fn () => user()?->can(SubuserPermission::UserCreate, $server))
->schema([ ->schema([
Grid::make() Grid::make()
->columnSpanFull() ->columnSpanFull()
@ -266,7 +274,7 @@ class SubuserResource extends Resource
$permissions = collect($data) $permissions = collect($data)
->forget('email') ->forget('email')
->flatMap(fn ($permissions, $key) => collect($permissions)->map(fn ($permission) => "$key.$permission")) ->flatMap(fn ($permissions, $key) => collect($permissions)->map(fn ($permission) => "$key.$permission"))
->push(Permission::ACTION_WEBSOCKET_CONNECT) ->push(SubuserPermission::WebsocketConnect->value)
->unique() ->unique()
->all(); ->all();

View File

@ -2,9 +2,9 @@
namespace App\Filament\Server\Widgets; namespace App\Filament\Server\Widgets;
use App\Enums\SubuserPermission;
use App\Exceptions\Http\HttpForbiddenException; use App\Exceptions\Http\HttpForbiddenException;
use App\Livewire\AlertBanner; use App\Livewire\AlertBanner;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\User; use App\Models\User;
use App\Services\Nodes\NodeJWTService; use App\Services\Nodes\NodeJWTService;
@ -46,7 +46,7 @@ class ServerConsole extends Widget
protected function getToken(): string protected function getToken(): string
{ {
if (!$this->user || !$this->server || $this->user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $this->server)) { if (!$this->user || !$this->server || $this->user->cannot(SubuserPermission::WebsocketConnect, $this->server)) {
throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.'); throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.');
} }
@ -72,7 +72,7 @@ class ServerConsole extends Widget
protected function authorizeSendCommand(): bool protected function authorizeSendCommand(): bool
{ {
return $this->user->can(Permission::ACTION_CONTROL_CONSOLE, $this->server); return $this->user->can(SubuserPermission::ControlConsole, $this->server);
} }
protected function canSendCommand(): bool protected function canSendCommand(): bool

View File

@ -4,13 +4,12 @@ namespace App\Http\Controllers\Api\Client;
use App\Http\Requests\Api\Client\GetServersRequest; use App\Http\Requests\Api\Client\GetServersRequest;
use App\Models\Filters\MultiFieldServerFilter; use App\Models\Filters\MultiFieldServerFilter;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser;
use App\Transformers\Api\Client\ServerTransformer; use App\Transformers\Api\Client\ServerTransformer;
use Dedoc\Scramble\Attributes\Group; use Dedoc\Scramble\Attributes\Group;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Spatie\QueryBuilder\AllowedFilter; use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\QueryBuilder;
@ -81,14 +80,14 @@ class ClientController extends ClientApiController
* *
* Returns all the subuser permissions available on the system. * Returns all the subuser permissions available on the system.
* *
* @return array{object: string, attributes: array{permissions: Collection}} * @return array{object: string, attributes: array{permissions: string[]}}
*/ */
public function permissions(): array public function permissions(): array
{ {
return [ return [
'object' => 'system_permissions', 'object' => 'system_permissions',
'attributes' => [ 'attributes' => [
'permissions' => Permission::permissions(), 'permissions' => Subuser::allPermissionKeys(),
], ],
]; ];
} }

View File

@ -2,10 +2,10 @@
namespace App\Http\Controllers\Api\Client\Servers; namespace App\Http\Controllers\Api\Client\Servers;
use App\Enums\SubuserPermission;
use App\Http\Controllers\Api\Client\ClientApiController; use App\Http\Controllers\Api\Client\ClientApiController;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\ActivityLog; use App\Models\ActivityLog;
use App\Models\Permission;
use App\Models\Role; use App\Models\Role;
use App\Models\Server; use App\Models\Server;
use App\Models\User; use App\Models\User;
@ -29,7 +29,7 @@ class ActivityLogController extends ClientApiController
*/ */
public function __invoke(ClientApiRequest $request, Server $server): array public function __invoke(ClientApiRequest $request, Server $server): array
{ {
Gate::authorize(Permission::ACTION_ACTIVITY_READ, $server); Gate::authorize(SubuserPermission::ActivityRead, $server);
$activity = QueryBuilder::for($server->activity()) $activity = QueryBuilder::for($server->activity())
->allowedSorts(['timestamp']) ->allowedSorts(['timestamp'])

View File

@ -3,13 +3,13 @@
namespace App\Http\Controllers\Api\Client\Servers; namespace App\Http\Controllers\Api\Client\Servers;
use App\Enums\ServerState; use App\Enums\ServerState;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Http\Controllers\Api\Client\ClientApiController; use App\Http\Controllers\Api\Client\ClientApiController;
use App\Http\Requests\Api\Client\Servers\Backups\RenameBackupRequest; use App\Http\Requests\Api\Client\Servers\Backups\RenameBackupRequest;
use App\Http\Requests\Api\Client\Servers\Backups\RestoreBackupRequest; use App\Http\Requests\Api\Client\Servers\Backups\RestoreBackupRequest;
use App\Http\Requests\Api\Client\Servers\Backups\StoreBackupRequest; use App\Http\Requests\Api\Client\Servers\Backups\StoreBackupRequest;
use App\Models\Backup; use App\Models\Backup;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Repositories\Daemon\DaemonBackupRepository; use App\Repositories\Daemon\DaemonBackupRepository;
use App\Services\Backups\DeleteBackupService; use App\Services\Backups\DeleteBackupService;
@ -48,7 +48,7 @@ class BackupController extends ClientApiController
*/ */
public function index(Request $request, Server $server): array public function index(Request $request, Server $server): array
{ {
if (!$request->user()->can(Permission::ACTION_BACKUP_READ, $server)) { if (!$request->user()->can(SubuserPermission::BackupRead, $server)) {
throw new AuthorizationException(); throw new AuthorizationException();
} }
@ -82,7 +82,7 @@ class BackupController extends ClientApiController
// otherwise ignore this status. This gets a little funky since it isn't clear // otherwise ignore this status. This gets a little funky since it isn't clear
// how best to allow a user to create a backup that is locked without also preventing // how best to allow a user to create a backup that is locked without also preventing
// them from just filling up a server with backups that can never be deleted? // them from just filling up a server with backups that can never be deleted?
if ($request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { if ($request->user()->can(SubuserPermission::BackupDelete, $server)) {
$action->setIsLocked((bool) $request->input('is_locked')); $action->setIsLocked((bool) $request->input('is_locked'));
} }
@ -110,7 +110,7 @@ class BackupController extends ClientApiController
*/ */
public function toggleLock(Request $request, Server $server, Backup $backup): array public function toggleLock(Request $request, Server $server, Backup $backup): array
{ {
if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { if (!$request->user()->can(SubuserPermission::BackupDelete, $server)) {
throw new AuthorizationException(); throw new AuthorizationException();
} }
@ -136,7 +136,7 @@ class BackupController extends ClientApiController
*/ */
public function view(Request $request, Server $server, Backup $backup): array public function view(Request $request, Server $server, Backup $backup): array
{ {
if (!$request->user()->can(Permission::ACTION_BACKUP_READ, $server)) { if (!$request->user()->can(SubuserPermission::BackupRead, $server)) {
throw new AuthorizationException(); throw new AuthorizationException();
} }
@ -155,7 +155,7 @@ class BackupController extends ClientApiController
*/ */
public function delete(Request $request, Server $server, Backup $backup): JsonResponse public function delete(Request $request, Server $server, Backup $backup): JsonResponse
{ {
if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { if (!$request->user()->can(SubuserPermission::BackupDelete, $server)) {
throw new AuthorizationException(); throw new AuthorizationException();
} }
@ -181,7 +181,7 @@ class BackupController extends ClientApiController
*/ */
public function download(Request $request, Server $server, Backup $backup): JsonResponse public function download(Request $request, Server $server, Backup $backup): JsonResponse
{ {
if (!$request->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) { if (!$request->user()->can(SubuserPermission::BackupDownload, $server)) {
throw new AuthorizationException(); throw new AuthorizationException();
} }

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Api\Client\Servers; namespace App\Http\Controllers\Api\Client\Servers;
use App\Enums\SubuserPermission;
use App\Exceptions\Http\HttpForbiddenException; use App\Exceptions\Http\HttpForbiddenException;
use App\Exceptions\Model\DataValidationException; use App\Exceptions\Model\DataValidationException;
use App\Exceptions\Service\ServiceLimitExceededException; use App\Exceptions\Service\ServiceLimitExceededException;
@ -9,7 +10,6 @@ use App\Facades\Activity;
use App\Http\Controllers\Api\Client\ClientApiController; use App\Http\Controllers\Api\Client\ClientApiController;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Http\Requests\Api\Client\Servers\Schedules\StoreTaskRequest; use App\Http\Requests\Api\Client\Servers\Schedules\StoreTaskRequest;
use App\Models\Permission;
use App\Models\Schedule; use App\Models\Schedule;
use App\Models\Server; use App\Models\Server;
use App\Models\Task; use App\Models\Task;
@ -170,7 +170,7 @@ class ScheduleTaskController extends ClientApiController
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
if (!$request->user()->can(Permission::ACTION_SCHEDULE_DELETE, $server)) { if (!$request->user()->can(SubuserPermission::ScheduleDelete, $server)) {
throw new HttpForbiddenException('You do not have permission to perform this action.'); throw new HttpForbiddenException('You do not have permission to perform this action.');
} }

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Api\Client\Servers; namespace App\Http\Controllers\Api\Client\Servers;
use App\Enums\SubuserPermission;
use App\Exceptions\Model\DataValidationException; use App\Exceptions\Model\DataValidationException;
use App\Exceptions\Service\Subuser\ServerSubuserExistsException; use App\Exceptions\Service\Subuser\ServerSubuserExistsException;
use App\Exceptions\Service\Subuser\UserIsServerOwnerException; use App\Exceptions\Service\Subuser\UserIsServerOwnerException;
@ -11,7 +12,6 @@ use App\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest;
use App\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest; use App\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest;
use App\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest; use App\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest;
use App\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest; use App\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser; use App\Models\Subuser;
use App\Models\User; use App\Models\User;
@ -82,18 +82,17 @@ class SubuserController extends ClientApiController
*/ */
public function store(StoreSubuserRequest $request, Server $server): array public function store(StoreSubuserRequest $request, Server $server): array
{ {
$response = $this->creationService->handle( $email = $request->input('email');
$server, $permissions = $this->getCleanedPermissions($request);
$request->input('email'),
$this->getDefaultPermissions($request) $subuser = $this->creationService->handle($server, $email, $permissions);
);
Activity::event('server:subuser.create') Activity::event('server:subuser.create')
->subject($response->user) ->subject($subuser->user)
->property(['email' => $request->input('email'), 'permissions' => $this->getDefaultPermissions($request)]) ->property(['email' => $email, 'permissions' => $subuser->permissions])
->log(); ->log();
return $this->fractal->item($response) return $this->fractal->item($subuser)
->transformWith($this->getTransformer(SubuserTransformer::class)) ->transformWith($this->getTransformer(SubuserTransformer::class))
->toArray(); ->toArray();
} }
@ -112,7 +111,7 @@ class SubuserController extends ClientApiController
/** @var Subuser $subuser */ /** @var Subuser $subuser */
$subuser = $request->attributes->get('subuser'); $subuser = $request->attributes->get('subuser');
$this->updateService->handle($subuser, $server, $this->getDefaultPermissions($request)); $this->updateService->handle($subuser, $server, $this->getCleanedPermissions($request));
return $this->fractal->item($subuser->refresh()) return $this->fractal->item($subuser->refresh())
->transformWith($this->getTransformer(SubuserTransformer::class)) ->transformWith($this->getTransformer(SubuserTransformer::class))
@ -135,17 +134,19 @@ class SubuserController extends ClientApiController
} }
/** /**
* Returns the default permissions for subusers and parses out any permissions * Returns the "cleaned" permissions for subusers and parses out any permissions
* that were passed that do not also exist in the internally tracked list of * that were passed that do not also exist in the internally tracked list of
* permissions. * permissions.
* *
* @return array<array-key, mixed> * @return string[]
*/ */
protected function getDefaultPermissions(Request $request): array protected function getCleanedPermissions(Request $request): array
{ {
$allowed = Permission::permissionKeys()->all(); return collect($request->input('permissions') ?? [])
$cleaned = array_intersect($request->input('permissions') ?? [], $allowed); ->intersect(Subuser::allPermissionKeys())
->push(SubuserPermission::WebsocketConnect->value)
return array_unique(array_merge($cleaned, [Permission::ACTION_WEBSOCKET_CONNECT])); ->unique()
->values()
->toArray();
} }
} }

View File

@ -2,10 +2,10 @@
namespace App\Http\Controllers\Api\Client\Servers; namespace App\Http\Controllers\Api\Client\Servers;
use App\Enums\SubuserPermission;
use App\Exceptions\Http\HttpForbiddenException; use App\Exceptions\Http\HttpForbiddenException;
use App\Http\Controllers\Api\Client\ClientApiController; use App\Http\Controllers\Api\Client\ClientApiController;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Services\Nodes\NodeJWTService; use App\Services\Nodes\NodeJWTService;
use App\Services\Servers\GetUserPermissionsService; use App\Services\Servers\GetUserPermissionsService;
@ -37,7 +37,7 @@ class WebsocketController extends ClientApiController
public function __invoke(ClientApiRequest $request, Server $server): JsonResponse public function __invoke(ClientApiRequest $request, Server $server): JsonResponse
{ {
$user = $request->user(); $user = $request->user();
if ($user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $server)) { if ($user->cannot(SubuserPermission::WebsocketConnect, $server)) {
throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.'); throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.');
} }

View File

@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\Remote; namespace App\Http\Controllers\Api\Remote;
use App\Enums\SubuserPermission;
use App\Exceptions\Http\HttpForbiddenException; use App\Exceptions\Http\HttpForbiddenException;
use App\Facades\Activity; use App\Facades\Activity;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Api\Remote\SftpAuthenticationFormRequest; use App\Http\Requests\Api\Remote\SftpAuthenticationFormRequest;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\User; use App\Models\User;
use App\Services\Servers\GetUserPermissionsService; use App\Services\Servers\GetUserPermissionsService;
@ -141,7 +141,7 @@ class SftpAuthenticationController extends Controller
if ($user->cannot('update server', $server) && $server->owner_id !== $user->id) { if ($user->cannot('update server', $server) && $server->owner_id !== $user->id) {
$permissions = $this->permissions->handle($server, $user); $permissions = $this->permissions->handle($server, $user);
if (!in_array(Permission::ACTION_FILE_SFTP, $permissions)) { if (!in_array(SubuserPermission::FileSftp->value, $permissions)) {
Activity::event('server:sftp.denied')->actor($user)->subject($server)->log(); Activity::event('server:sftp.denied')->actor($user)->subject($server)->log();
throw new HttpForbiddenException('You do not have permission to access SFTP for this server.'); throw new HttpForbiddenException('You do not have permission to access SFTP for this server.');

View File

@ -2,14 +2,14 @@
namespace App\Http\Requests\Api\Client\Servers\Backups; namespace App\Http\Requests\Api\Client\Servers\Backups;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class RenameBackupRequest extends ClientApiRequest class RenameBackupRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_BACKUP_DELETE; return SubuserPermission::BackupDelete;
} }
public function rules(): array public function rules(): array

View File

@ -2,14 +2,14 @@
namespace App\Http\Requests\Api\Client\Servers\Backups; namespace App\Http\Requests\Api\Client\Servers\Backups;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class RestoreBackupRequest extends ClientApiRequest class RestoreBackupRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_BACKUP_RESTORE; return SubuserPermission::BackupRestore;
} }
public function rules(): array public function rules(): array

View File

@ -2,14 +2,14 @@
namespace App\Http\Requests\Api\Client\Servers\Backups; namespace App\Http\Requests\Api\Client\Servers\Backups;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class StoreBackupRequest extends ClientApiRequest class StoreBackupRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_BACKUP_CREATE; return SubuserPermission::BackupCreate;
} }
public function rules(): array public function rules(): array

View File

@ -3,13 +3,13 @@
namespace App\Http\Requests\Api\Client\Servers\Databases; namespace App\Http\Requests\Api\Client\Servers\Databases;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class DeleteDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest class DeleteDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_DATABASE_DELETE; return SubuserPermission::DatabaseDelete;
} }
} }

View File

@ -3,13 +3,13 @@
namespace App\Http\Requests\Api\Client\Servers\Databases; namespace App\Http\Requests\Api\Client\Servers\Databases;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class GetDatabasesRequest extends ClientApiRequest implements ClientPermissionsRequest class GetDatabasesRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_DATABASE_READ; return SubuserPermission::DatabaseRead;
} }
} }

View File

@ -2,16 +2,16 @@
namespace App\Http\Requests\Api\Client\Servers\Databases; namespace App\Http\Requests\Api\Client\Servers\Databases;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class RotatePasswordRequest extends ClientApiRequest class RotatePasswordRequest extends ClientApiRequest
{ {
/** /**
* Check that the user has permission to rotate the password. * Check that the user has permission to rotate the password.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_DATABASE_UPDATE; return SubuserPermission::DatabaseUpdate;
} }
} }

View File

@ -3,9 +3,9 @@
namespace App\Http\Requests\Api\Client\Servers\Databases; namespace App\Http\Requests\Api\Client\Servers\Databases;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Database; use App\Models\Database;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Services\Databases\DatabaseManagementService; use App\Services\Databases\DatabaseManagementService;
use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\Builder;
@ -14,9 +14,9 @@ use Webmozart\Assert\Assert;
class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_DATABASE_CREATE; return SubuserPermission::DatabaseCreate;
} }
public function rules(): array public function rules(): array

View File

@ -3,14 +3,14 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class ChmodFilesRequest extends ClientApiRequest implements ClientPermissionsRequest class ChmodFilesRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_UPDATE; return SubuserPermission::FileUpdate;
} }
public function rules(): array public function rules(): array

View File

@ -2,17 +2,17 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class CompressFilesRequest extends ClientApiRequest class CompressFilesRequest extends ClientApiRequest
{ {
/** /**
* Checks that the authenticated user is allowed to create archives for this server. * Checks that the authenticated user is allowed to create archives for this server.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_ARCHIVE; return SubuserPermission::FileArchive;
} }
public function rules(): array public function rules(): array

View File

@ -3,14 +3,14 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class CopyFileRequest extends ClientApiRequest implements ClientPermissionsRequest class CopyFileRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_CREATE; return SubuserPermission::FileCreate;
} }
public function rules(): array public function rules(): array

View File

@ -2,17 +2,17 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class CreateFolderRequest extends ClientApiRequest class CreateFolderRequest extends ClientApiRequest
{ {
/** /**
* Checks that the authenticated user is allowed to create files on the server. * Checks that the authenticated user is allowed to create files on the server.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_CREATE; return SubuserPermission::FileCreate;
} }
public function rules(): array public function rules(): array

View File

@ -2,8 +2,8 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class DecompressFilesRequest extends ClientApiRequest class DecompressFilesRequest extends ClientApiRequest
{ {
@ -12,9 +12,9 @@ class DecompressFilesRequest extends ClientApiRequest
* rely on the archive permission here as it makes more sense to make sure the user can create * rely on the archive permission here as it makes more sense to make sure the user can create
* additional files rather than make an archive. * additional files rather than make an archive.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_CREATE; return SubuserPermission::FileCreate;
} }
public function rules(): array public function rules(): array

View File

@ -3,14 +3,14 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class DeleteFileRequest extends ClientApiRequest implements ClientPermissionsRequest class DeleteFileRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_DELETE; return SubuserPermission::FileDelete;
} }
/** /**

View File

@ -3,8 +3,8 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class GetFileContentsRequest extends ClientApiRequest implements ClientPermissionsRequest class GetFileContentsRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
@ -13,9 +13,9 @@ class GetFileContentsRequest extends ClientApiRequest implements ClientPermissio
* validate that the authenticated user has permission to perform this action aganist * validate that the authenticated user has permission to perform this action aganist
* the given resource (server). * the given resource (server).
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_READ_CONTENT; return SubuserPermission::FileReadContent;
} }
public function rules(): array public function rules(): array

View File

@ -2,8 +2,8 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class ListFilesRequest extends ClientApiRequest class ListFilesRequest extends ClientApiRequest
{ {
@ -11,9 +11,9 @@ class ListFilesRequest extends ClientApiRequest
* Check that the user making this request to the API is authorized to list all * Check that the user making this request to the API is authorized to list all
* the files that exist for a given server. * the files that exist for a given server.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_READ; return SubuserPermission::FileRead;
} }
public function rules(): array public function rules(): array

View File

@ -3,14 +3,14 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class PullFileRequest extends ClientApiRequest implements ClientPermissionsRequest class PullFileRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_CREATE; return SubuserPermission::FileCreate;
} }
public function rules(): array public function rules(): array

View File

@ -3,8 +3,8 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class RenameFileRequest extends ClientApiRequest implements ClientPermissionsRequest class RenameFileRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
@ -12,9 +12,9 @@ class RenameFileRequest extends ClientApiRequest implements ClientPermissionsReq
* The permission the user is required to have in order to perform this * The permission the user is required to have in order to perform this
* request action. * request action.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_UPDATE; return SubuserPermission::FileUpdate;
} }
public function rules(): array public function rules(): array

View File

@ -2,13 +2,13 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class UploadFileRequest extends ClientApiRequest class UploadFileRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_CREATE; return SubuserPermission::FileCreate;
} }
} }

View File

@ -3,8 +3,8 @@
namespace App\Http\Requests\Api\Client\Servers\Files; namespace App\Http\Requests\Api\Client\Servers\Files;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class WriteFileContentRequest extends ClientApiRequest implements ClientPermissionsRequest class WriteFileContentRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
@ -13,9 +13,9 @@ class WriteFileContentRequest extends ClientApiRequest implements ClientPermissi
* validate that the authenticated user has permission to perform this action aganist * validate that the authenticated user has permission to perform this action aganist
* the given resource (server). * the given resource (server).
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_FILE_CREATE; return SubuserPermission::FileCreate;
} }
/** /**

View File

@ -2,13 +2,13 @@
namespace App\Http\Requests\Api\Client\Servers\Network; namespace App\Http\Requests\Api\Client\Servers\Network;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class DeleteAllocationRequest extends ClientApiRequest class DeleteAllocationRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_ALLOCATION_DELETE; return SubuserPermission::AllocationDelete;
} }
} }

View File

@ -2,8 +2,8 @@
namespace App\Http\Requests\Api\Client\Servers\Network; namespace App\Http\Requests\Api\Client\Servers\Network;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class GetNetworkRequest extends ClientApiRequest class GetNetworkRequest extends ClientApiRequest
{ {
@ -11,8 +11,8 @@ class GetNetworkRequest extends ClientApiRequest
* Check that the user has permission to view the allocations for * Check that the user has permission to view the allocations for
* this server. * this server.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_ALLOCATION_READ; return SubuserPermission::AllocationRead;
} }
} }

View File

@ -2,13 +2,13 @@
namespace App\Http\Requests\Api\Client\Servers\Network; namespace App\Http\Requests\Api\Client\Servers\Network;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class NewAllocationRequest extends ClientApiRequest class NewAllocationRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_ALLOCATION_CREATE; return SubuserPermission::AllocationCreate;
} }
} }

View File

@ -2,15 +2,15 @@
namespace App\Http\Requests\Api\Client\Servers\Network; namespace App\Http\Requests\Api\Client\Servers\Network;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Allocation; use App\Models\Allocation;
use App\Models\Permission;
class UpdateAllocationRequest extends ClientApiRequest class UpdateAllocationRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_ALLOCATION_UPDATE; return SubuserPermission::AllocationUpdate;
} }
public function rules(): array public function rules(): array

View File

@ -2,12 +2,12 @@
namespace App\Http\Requests\Api\Client\Servers\Schedules; namespace App\Http\Requests\Api\Client\Servers\Schedules;
use App\Models\Permission; use App\Enums\SubuserPermission;
class DeleteScheduleRequest extends ViewScheduleRequest class DeleteScheduleRequest extends ViewScheduleRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SCHEDULE_DELETE; return SubuserPermission::ScheduleDelete;
} }
} }

View File

@ -2,14 +2,14 @@
namespace App\Http\Requests\Api\Client\Servers\Schedules; namespace App\Http\Requests\Api\Client\Servers\Schedules;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\Schedule; use App\Models\Schedule;
class StoreScheduleRequest extends ViewScheduleRequest class StoreScheduleRequest extends ViewScheduleRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SCHEDULE_CREATE; return SubuserPermission::ScheduleCreate;
} }
public function rules(): array public function rules(): array

View File

@ -2,7 +2,7 @@
namespace App\Http\Requests\Api\Client\Servers\Schedules; namespace App\Http\Requests\Api\Client\Servers\Schedules;
use App\Models\Permission; use App\Enums\SubuserPermission;
class StoreTaskRequest extends ViewScheduleRequest class StoreTaskRequest extends ViewScheduleRequest
{ {
@ -11,9 +11,9 @@ class StoreTaskRequest extends ViewScheduleRequest
* check if they can modify a schedule to determine if they're able to do this. There * check if they can modify a schedule to determine if they're able to do this. There
* are no task specific permissions. * are no task specific permissions.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SCHEDULE_UPDATE; return SubuserPermission::ScheduleUpdate;
} }
public function rules(): array public function rules(): array

View File

@ -2,14 +2,14 @@
namespace App\Http\Requests\Api\Client\Servers\Schedules; namespace App\Http\Requests\Api\Client\Servers\Schedules;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class TriggerScheduleRequest extends ClientApiRequest class TriggerScheduleRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SCHEDULE_UPDATE; return SubuserPermission::ScheduleUpdate;
} }
public function rules(): array public function rules(): array

View File

@ -2,12 +2,12 @@
namespace App\Http\Requests\Api\Client\Servers\Schedules; namespace App\Http\Requests\Api\Client\Servers\Schedules;
use App\Models\Permission; use App\Enums\SubuserPermission;
class UpdateScheduleRequest extends StoreScheduleRequest class UpdateScheduleRequest extends StoreScheduleRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SCHEDULE_UPDATE; return SubuserPermission::ScheduleUpdate;
} }
} }

View File

@ -2,8 +2,8 @@
namespace App\Http\Requests\Api\Client\Servers\Schedules; namespace App\Http\Requests\Api\Client\Servers\Schedules;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
use App\Models\Schedule; use App\Models\Schedule;
use App\Models\Server; use App\Models\Server;
use App\Models\Task; use App\Models\Task;
@ -36,8 +36,8 @@ class ViewScheduleRequest extends ClientApiRequest
return true; return true;
} }
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SCHEDULE_READ; return SubuserPermission::ScheduleRead;
} }
} }

View File

@ -2,17 +2,17 @@
namespace App\Http\Requests\Api\Client\Servers; namespace App\Http\Requests\Api\Client\Servers;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class SendCommandRequest extends ClientApiRequest class SendCommandRequest extends ClientApiRequest
{ {
/** /**
* Determine if the API user has permission to perform this action. * Determine if the API user has permission to perform this action.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_CONTROL_CONSOLE; return SubuserPermission::ControlConsole;
} }
/** /**

View File

@ -2,28 +2,28 @@
namespace App\Http\Requests\Api\Client\Servers; namespace App\Http\Requests\Api\Client\Servers;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class SendPowerRequest extends ClientApiRequest class SendPowerRequest extends ClientApiRequest
{ {
/** /**
* Determine if the user has permission to send a power command to a server. * Determine if the user has permission to send a power command to a server.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
switch ($this->input('signal')) { switch ($this->input('signal')) {
case 'start': case 'start':
return Permission::ACTION_CONTROL_START; return SubuserPermission::ControlStart;
case 'stop': case 'stop':
case 'kill': case 'kill':
return Permission::ACTION_CONTROL_STOP; return SubuserPermission::ControlStop;
case 'restart': case 'restart':
return Permission::ACTION_CONTROL_RESTART; return SubuserPermission::ControlRestart;
} }
// Fallback for invalid signals // Fallback for invalid signals
return Permission::ACTION_WEBSOCKET_CONNECT; return SubuserPermission::WebsocketConnect;
} }
/** /**

View File

@ -3,8 +3,8 @@
namespace App\Http\Requests\Api\Client\Servers\Settings; namespace App\Http\Requests\Api\Client\Servers\Settings;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class DescriptionServerRequest extends ClientApiRequest implements ClientPermissionsRequest class DescriptionServerRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
@ -13,9 +13,9 @@ class DescriptionServerRequest extends ClientApiRequest implements ClientPermiss
* validate that the authenticated user has permission to perform this action against * validate that the authenticated user has permission to perform this action against
* the given resource (server). * the given resource (server).
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SETTINGS_DESCRIPTION; return SubuserPermission::SettingsDescription;
} }
/** /**

View File

@ -2,13 +2,13 @@
namespace App\Http\Requests\Api\Client\Servers\Settings; namespace App\Http\Requests\Api\Client\Servers\Settings;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class ReinstallServerRequest extends ClientApiRequest class ReinstallServerRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SETTINGS_REINSTALL; return SubuserPermission::SettingsReinstall;
} }
} }

View File

@ -3,8 +3,8 @@
namespace App\Http\Requests\Api\Client\Servers\Settings; namespace App\Http\Requests\Api\Client\Servers\Settings;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
class RenameServerRequest extends ClientApiRequest implements ClientPermissionsRequest class RenameServerRequest extends ClientApiRequest implements ClientPermissionsRequest
@ -14,9 +14,9 @@ class RenameServerRequest extends ClientApiRequest implements ClientPermissionsR
* validate that the authenticated user has permission to perform this action against * validate that the authenticated user has permission to perform this action against
* the given resource (server). * the given resource (server).
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_SETTINGS_RENAME; return SubuserPermission::SettingsRename;
} }
/** /**

View File

@ -3,17 +3,17 @@
namespace App\Http\Requests\Api\Client\Servers\Settings; namespace App\Http\Requests\Api\Client\Servers\Settings;
use App\Contracts\Http\ClientPermissionsRequest; use App\Contracts\Http\ClientPermissionsRequest;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
class SetDockerImageRequest extends ClientApiRequest implements ClientPermissionsRequest class SetDockerImageRequest extends ClientApiRequest implements ClientPermissionsRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_STARTUP_DOCKER_IMAGE; return SubuserPermission::StartupDockerImage;
} }
public function rules(): array public function rules(): array

View File

@ -2,13 +2,13 @@
namespace App\Http\Requests\Api\Client\Servers\Startup; namespace App\Http\Requests\Api\Client\Servers\Startup;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class GetStartupRequest extends ClientApiRequest class GetStartupRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_STARTUP_READ; return SubuserPermission::StartupRead;
} }
} }

View File

@ -2,14 +2,14 @@
namespace App\Http\Requests\Api\Client\Servers\Startup; namespace App\Http\Requests\Api\Client\Servers\Startup;
use App\Enums\SubuserPermission;
use App\Http\Requests\Api\Client\ClientApiRequest; use App\Http\Requests\Api\Client\ClientApiRequest;
use App\Models\Permission;
class UpdateStartupVariableRequest extends ClientApiRequest class UpdateStartupVariableRequest extends ClientApiRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_STARTUP_UPDATE; return SubuserPermission::StartupUpdate;
} }
/** /**

View File

@ -2,12 +2,12 @@
namespace App\Http\Requests\Api\Client\Servers\Subusers; namespace App\Http\Requests\Api\Client\Servers\Subusers;
use App\Models\Permission; use App\Enums\SubuserPermission;
class DeleteSubuserRequest extends SubuserRequest class DeleteSubuserRequest extends SubuserRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_USER_DELETE; return SubuserPermission::UserDelete;
} }
} }

View File

@ -2,15 +2,15 @@
namespace App\Http\Requests\Api\Client\Servers\Subusers; namespace App\Http\Requests\Api\Client\Servers\Subusers;
use App\Models\Permission; use App\Enums\SubuserPermission;
class GetSubuserRequest extends SubuserRequest class GetSubuserRequest extends SubuserRequest
{ {
/** /**
* Confirm that a user is able to view subusers for the specified server. * Confirm that a user is able to view subusers for the specified server.
*/ */
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_USER_READ; return SubuserPermission::UserRead;
} }
} }

View File

@ -2,13 +2,13 @@
namespace App\Http\Requests\Api\Client\Servers\Subusers; namespace App\Http\Requests\Api\Client\Servers\Subusers;
use App\Models\Permission; use App\Enums\SubuserPermission;
class StoreSubuserRequest extends SubuserRequest class StoreSubuserRequest extends SubuserRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_USER_CREATE; return SubuserPermission::UserCreate;
} }
public function rules(): array public function rules(): array

View File

@ -2,13 +2,13 @@
namespace App\Http\Requests\Api\Client\Servers\Subusers; namespace App\Http\Requests\Api\Client\Servers\Subusers;
use App\Models\Permission; use App\Enums\SubuserPermission;
class UpdateSubuserRequest extends SubuserRequest class UpdateSubuserRequest extends SubuserRequest
{ {
public function permission(): string public function permission(): SubuserPermission
{ {
return Permission::ACTION_USER_UPDATE; return SubuserPermission::UserUpdate;
} }
public function rules(): array public function rules(): array

View File

@ -1,221 +0,0 @@
<?php
namespace App\Models;
use App\Contracts\Validatable;
use App\Traits\HasValidation;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
class Permission extends Model implements Validatable
{
use HasFactory, HasValidation;
/**
* The resource name for this model when it is transformed into an
* API representation using fractal.
*/
public const RESOURCE_NAME = 'subuser_permission';
/**
* Constants defining different permissions available.
*/
public const ACTION_WEBSOCKET_CONNECT = 'websocket.connect';
public const ACTION_CONTROL_CONSOLE = 'control.console';
public const ACTION_CONTROL_START = 'control.start';
public const ACTION_CONTROL_STOP = 'control.stop';
public const ACTION_CONTROL_RESTART = 'control.restart';
public const ACTION_DATABASE_READ = 'database.read';
public const ACTION_DATABASE_CREATE = 'database.create';
public const ACTION_DATABASE_UPDATE = 'database.update';
public const ACTION_DATABASE_DELETE = 'database.delete';
public const ACTION_DATABASE_VIEW_PASSWORD = 'database.view-password';
public const ACTION_SCHEDULE_READ = 'schedule.read';
public const ACTION_SCHEDULE_CREATE = 'schedule.create';
public const ACTION_SCHEDULE_UPDATE = 'schedule.update';
public const ACTION_SCHEDULE_DELETE = 'schedule.delete';
public const ACTION_USER_READ = 'user.read';
public const ACTION_USER_CREATE = 'user.create';
public const ACTION_USER_UPDATE = 'user.update';
public const ACTION_USER_DELETE = 'user.delete';
public const ACTION_BACKUP_READ = 'backup.read';
public const ACTION_BACKUP_CREATE = 'backup.create';
public const ACTION_BACKUP_DELETE = 'backup.delete';
public const ACTION_BACKUP_DOWNLOAD = 'backup.download';
public const ACTION_BACKUP_RESTORE = 'backup.restore';
public const ACTION_ALLOCATION_READ = 'allocation.read';
public const ACTION_ALLOCATION_CREATE = 'allocation.create';
public const ACTION_ALLOCATION_UPDATE = 'allocation.update';
public const ACTION_ALLOCATION_DELETE = 'allocation.delete';
public const ACTION_FILE_READ = 'file.read';
public const ACTION_FILE_READ_CONTENT = 'file.read-content';
public const ACTION_FILE_CREATE = 'file.create';
public const ACTION_FILE_UPDATE = 'file.update';
public const ACTION_FILE_DELETE = 'file.delete';
public const ACTION_FILE_ARCHIVE = 'file.archive';
public const ACTION_FILE_SFTP = 'file.sftp';
public const ACTION_STARTUP_READ = 'startup.read';
public const ACTION_STARTUP_UPDATE = 'startup.update';
public const ACTION_STARTUP_DOCKER_IMAGE = 'startup.docker-image';
public const ACTION_SETTINGS_RENAME = 'settings.rename';
public const ACTION_SETTINGS_DESCRIPTION = 'settings.description';
public const ACTION_SETTINGS_REINSTALL = 'settings.reinstall';
public const ACTION_ACTIVITY_READ = 'activity.read';
public $timestamps = false;
/**
* Fields that are not mass assignable.
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
/** @var array<array-key, string[]> */
public static array $validationRules = [
'subuser_id' => ['required', 'numeric', 'min:1'],
'permission' => ['required', 'string'],
];
protected function casts(): array
{
return [
'subuser_id' => 'integer',
];
}
/**
* All the permissions available on the system.
*
* @return array<int, array{
* name: string,
* icon: string,
* permissions: string[]
* }>
*/
public static function permissionData(): array
{
return [
[
'name' => 'control',
'icon' => 'tabler-terminal-2',
'permissions' => ['console', 'start', 'stop', 'restart'],
],
[
'name' => 'user',
'icon' => 'tabler-users',
'permissions' => ['read', 'create', 'update', 'delete'],
],
[
'name' => 'file',
'icon' => 'tabler-files',
'permissions' => ['read', 'read-content', 'create', 'update', 'delete', 'archive', 'sftp'],
],
[
'name' => 'backup',
'icon' => 'tabler-file-zip',
'permissions' => ['read', 'create', 'delete', 'download', 'restore'],
],
[
'name' => 'allocation',
'icon' => 'tabler-network',
'permissions' => ['read', 'create', 'update', 'delete'],
],
[
'name' => 'startup',
'icon' => 'tabler-player-play',
'permissions' => ['read', 'update', 'docker-image'],
],
[
'name' => 'database',
'icon' => 'tabler-database',
'permissions' => ['read', 'create', 'update', 'delete', 'view-password'],
],
[
'name' => 'schedule',
'icon' => 'tabler-clock',
'permissions' => ['read', 'create', 'update', 'delete'],
],
[
'name' => 'settings',
'icon' => 'tabler-settings',
'permissions' => ['rename', 'description', 'reinstall'],
],
[
'name' => 'activity',
'icon' => 'tabler-stack',
'permissions' => ['read'],
],
];
}
/**
* Returns all the permissions available on the system for a user to have when controlling a server.
*/
public static function permissions(): Collection
{
$permissions = [
'websocket' => [
'description' => 'Allows the user to connect to the server websocket, giving them access to view console output and realtime server stats.',
'keys' => [
'connect' => 'Allows a user to connect to the websocket instance for a server to stream the console.',
],
],
];
foreach (static::permissionData() as $data) {
$permissions[$data['name']] = [
'description' => trans('server/users.permissions.' . $data['name'] . '_desc'),
'keys' => collect($data['permissions'])->mapWithKeys(fn ($key) => [$key => trans('server/users.permissions.' . $data['name'] . '_' . str($key)->replace('-', '_'))])->toArray(),
];
}
return collect($permissions);
}
public static function permissionKeys(): Collection
{
return static::permissions()
->map(fn ($value, $prefix) => array_map(fn ($value) => "$prefix.$value", array_keys($value['keys'])))
->flatten();
}
}

View File

@ -3,12 +3,12 @@
namespace App\Models; namespace App\Models;
use App\Contracts\Validatable; use App\Contracts\Validatable;
use App\Enums\SubuserPermission;
use App\Traits\HasValidation; use App\Traits\HasValidation;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
/** /**
@ -33,6 +33,28 @@ class Subuser extends Model implements Validatable
*/ */
public const RESOURCE_NAME = 'server_subuser'; public const RESOURCE_NAME = 'server_subuser';
/** @var array<string, array{name: string, hidden: ?bool, icon: ?string, permissions: string[]}> */
protected static array $customPermissions = [];
/** @param string[] $permissions */
public static function registerCustomPermissions(string $name, array $permissions, ?string $icon = null, ?bool $hidden = null): void
{
$customPermission = static::$customPermissions[$name] ?? [];
$customPermission['name'] = $name;
$customPermission['permissions'] = array_merge($customPermission['permissions'] ?? [], $permissions);
if (!is_null($icon)) {
$customPermission['icon'] = $icon;
}
if (!is_null($hidden)) {
$customPermission['hidden'] = $hidden;
}
static::$customPermissions[$name] = $customPermission;
}
/** /**
* Fields that are not mass assignable. * Fields that are not mass assignable.
*/ */
@ -71,11 +93,56 @@ class Subuser extends Model implements Validatable
return $this->belongsTo(User::class); return $this->belongsTo(User::class);
} }
/** /** @return array<array{name: string, hidden: bool, icon: string, permissions: string[]}> */
* Gets the permissions associated with a subuser. public static function allPermissionData(): array
*/
public function permissions(): HasMany
{ {
return $this->hasMany(Permission::class); $allPermissions = [];
foreach (SubuserPermission::cases() as $subuserPermission) {
[$group, $permission] = $subuserPermission->split();
$allPermissions[$group] = [
'name' => $group,
'hidden' => $subuserPermission->isHidden(),
'icon' => $subuserPermission->getIcon(),
'permissions' => array_merge($allPermissions[$group]['permissions'] ?? [], [$permission]),
];
}
foreach (static::$customPermissions as $customPermission) {
$name = $customPermission['name'];
$groupData = $allPermissions[$name] ?? [];
$groupData = [
'name' => $name,
'hidden' => $customPermission['hidden'] ?? $groupData['hidden'] ?? false,
'icon' => $customPermission['icon'] ?? $groupData['icon'],
'permissions' => array_unique(array_merge($groupData['permissions'] ?? [], $customPermission['permissions'])),
];
$allPermissions[$name] = $groupData;
}
return array_values($allPermissions);
}
/** @return string[] */
public static function allPermissionKeys(): array
{
return collect(static::allPermissionData())
->map(fn ($data) => array_map(fn ($permission) => $data['name'] . '.' . $permission, $data['permissions']))
->flatten()
->unique()
->toArray();
}
public static function doesPermissionExist(string|SubuserPermission $permission): bool
{
if ($permission instanceof SubuserPermission) {
$permission = $permission->value;
}
return str_contains($permission, '.') && in_array($permission, static::allPermissionKeys());
} }
} }

View File

@ -4,6 +4,7 @@ namespace App\Models;
use App\Contracts\Validatable; use App\Contracts\Validatable;
use App\Enums\CustomizationKey; use App\Enums\CustomizationKey;
use App\Enums\SubuserPermission;
use App\Exceptions\DisplayException; use App\Exceptions\DisplayException;
use App\Extensions\Avatar\AvatarService; use App\Extensions\Avatar\AvatarService;
use App\Models\Traits\HasAccessTokens; use App\Models\Traits\HasAccessTokens;
@ -327,14 +328,18 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
return !$key ? $customization : $customization[$key->value]; return !$key ? $customization : $customization[$key->value];
} }
protected function checkPermission(Server $server, string $permission = ''): bool protected function checkPermission(Server $server, string|SubuserPermission $permission = ''): bool
{ {
if ($permission instanceof SubuserPermission) {
$permission = $permission->value;
}
if ($this->canned('update', $server) || $server->owner_id === $this->id) { if ($this->canned('update', $server) || $server->owner_id === $this->id) {
return true; return true;
} }
// If the user only has "view" permissions allow viewing the console // If the user only has "view" permissions allow viewing the console
if ($permission === Permission::ACTION_WEBSOCKET_CONNECT && $this->canned('view', $server)) { if ($permission === SubuserPermission::WebsocketConnect->value && $this->canned('view', $server)) {
return true; return true;
} }
@ -356,15 +361,11 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
*/ */
public function can($abilities, mixed $arguments = []): bool public function can($abilities, mixed $arguments = []): bool
{ {
if (is_string($abilities) && str_contains($abilities, '.')) {
[$permission, $key] = str($abilities)->explode('.', 2);
if (isset(Permission::permissions()[$permission]['keys'][$key])) {
if ($arguments instanceof Server) { if ($arguments instanceof Server) {
if ($abilities instanceof SubuserPermission || Subuser::doesPermissionExist($abilities)) {
return $this->checkPermission($arguments, $abilities); return $this->checkPermission($arguments, $abilities);
} }
} }
}
return $this->canned($abilities, $arguments); return $this->canned($abilities, $arguments);
} }

View File

@ -2,7 +2,7 @@
namespace App\Policies; namespace App\Policies;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\User; use App\Models\User;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -11,11 +11,11 @@ class ActivityLogPolicy
{ {
public function viewAny(User $user): bool public function viewAny(User $user): bool
{ {
return $user->can(Permission::ACTION_ACTIVITY_READ, Filament::getTenant()); return $user->can(SubuserPermission::ActivityRead, Filament::getTenant());
} }
public function view(User $user, Model $model): bool public function view(User $user, Model $model): bool
{ {
return $user->can(Permission::ACTION_ACTIVITY_READ, Filament::getTenant()); return $user->can(SubuserPermission::ActivityRead, Filament::getTenant());
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Policies; namespace App\Policies;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\User; use App\Models\User;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -11,26 +11,26 @@ class AllocationPolicy
{ {
public function viewAny(User $user): bool public function viewAny(User $user): bool
{ {
return $user->can(Permission::ACTION_ALLOCATION_READ, Filament::getTenant()); return $user->can(SubuserPermission::AllocationRead, Filament::getTenant());
} }
public function view(User $user, Model $record): bool public function view(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_ALLOCATION_READ, Filament::getTenant()); return $user->can(SubuserPermission::AllocationRead, Filament::getTenant());
} }
public function create(User $user): bool public function create(User $user): bool
{ {
return $user->can(Permission::ACTION_ALLOCATION_CREATE, Filament::getTenant()); return $user->can(SubuserPermission::AllocationCreate, Filament::getTenant());
} }
public function edit(User $user, Model $record): bool public function edit(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_ALLOCATION_UPDATE, Filament::getTenant()); return $user->can(SubuserPermission::AllocationUpdate, Filament::getTenant());
} }
public function delete(User $user, Model $record): bool public function delete(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_ALLOCATION_DELETE, Filament::getTenant()); return $user->can(SubuserPermission::AllocationDelete, Filament::getTenant());
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Policies; namespace App\Policies;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\User; use App\Models\User;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -11,21 +11,21 @@ class BackupPolicy
{ {
public function viewAny(User $user): bool public function viewAny(User $user): bool
{ {
return $user->can(Permission::ACTION_BACKUP_READ, Filament::getTenant()); return $user->can(SubuserPermission::BackupRead, Filament::getTenant());
} }
public function view(User $user, Model $record): bool public function view(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_BACKUP_READ, Filament::getTenant()); return $user->can(SubuserPermission::BackupRead, Filament::getTenant());
} }
public function create(User $user): bool public function create(User $user): bool
{ {
return $user->can(Permission::ACTION_BACKUP_CREATE, Filament::getTenant()); return $user->can(SubuserPermission::BackupCreate, Filament::getTenant());
} }
public function delete(User $user, Model $record): bool public function delete(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_BACKUP_DELETE, Filament::getTenant()); return $user->can(SubuserPermission::BackupDelete, Filament::getTenant());
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Policies; namespace App\Policies;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\User; use App\Models\User;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -11,26 +11,26 @@ class DatabasePolicy
{ {
public function viewAny(User $user): bool public function viewAny(User $user): bool
{ {
return $user->can(Permission::ACTION_DATABASE_READ, Filament::getTenant()); return $user->can(SubuserPermission::DatabaseRead, Filament::getTenant());
} }
public function view(User $user, Model $record): bool public function view(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_DATABASE_READ, Filament::getTenant()); return $user->can(SubuserPermission::DatabaseRead, Filament::getTenant());
} }
public function create(User $user): bool public function create(User $user): bool
{ {
return $user->can(Permission::ACTION_DATABASE_CREATE, Filament::getTenant()); return $user->can(SubuserPermission::DatabaseCreate, Filament::getTenant());
} }
public function edit(User $user, Model $record): bool public function edit(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_DATABASE_UPDATE, Filament::getTenant()); return $user->can(SubuserPermission::DatabaseUpdate, Filament::getTenant());
} }
public function delete(User $user, Model $record): bool public function delete(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_DATABASE_DELETE, Filament::getTenant()); return $user->can(SubuserPermission::DatabaseDelete, Filament::getTenant());
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Policies; namespace App\Policies;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\User; use App\Models\User;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -11,26 +11,26 @@ class FilePolicy
{ {
public function viewAny(User $user): bool public function viewAny(User $user): bool
{ {
return $user->can(Permission::ACTION_FILE_READ, Filament::getTenant()); return $user->can(SubuserPermission::FileRead, Filament::getTenant());
} }
public function view(User $user, Model $record): bool public function view(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()); return $user->can(SubuserPermission::FileReadContent, Filament::getTenant());
} }
public function create(User $user): bool public function create(User $user): bool
{ {
return $user->can(Permission::ACTION_FILE_CREATE, Filament::getTenant()); return $user->can(SubuserPermission::FileCreate, Filament::getTenant());
} }
public function edit(User $user, Model $record): bool public function edit(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_FILE_UPDATE, Filament::getTenant()); return $user->can(SubuserPermission::FileUpdate, Filament::getTenant());
} }
public function delete(User $user, Model $record): bool public function delete(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_FILE_DELETE, Filament::getTenant()); return $user->can(SubuserPermission::FileDelete, Filament::getTenant());
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Policies; namespace App\Policies;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\User; use App\Models\User;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -11,26 +11,26 @@ class SchedulePolicy
{ {
public function viewAny(User $user): bool public function viewAny(User $user): bool
{ {
return $user->can(Permission::ACTION_SCHEDULE_READ, Filament::getTenant()); return $user->can(SubuserPermission::ScheduleRead, Filament::getTenant());
} }
public function view(User $user, Model $record): bool public function view(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_SCHEDULE_READ, Filament::getTenant()); return $user->can(SubuserPermission::ScheduleRead, Filament::getTenant());
} }
public function create(User $user): bool public function create(User $user): bool
{ {
return $user->can(Permission::ACTION_SCHEDULE_CREATE, Filament::getTenant()); return $user->can(SubuserPermission::ScheduleCreate, Filament::getTenant());
} }
public function edit(User $user, Model $record): bool public function edit(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant()); return $user->can(SubuserPermission::ScheduleUpdate, Filament::getTenant());
} }
public function delete(User $user, Model $record): bool public function delete(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_SCHEDULE_DELETE, Filament::getTenant()); return $user->can(SubuserPermission::ScheduleDelete, Filament::getTenant());
} }
} }

View File

@ -2,8 +2,8 @@
namespace App\Policies; namespace App\Policies;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser;
use App\Models\User; use App\Models\User;
class ServerPolicy class ServerPolicy
@ -22,7 +22,7 @@ class ServerPolicy
return null; return null;
} }
if (Permission::permissionKeys()->contains($ability)) { if (Subuser::doesPermissionExist($ability)) {
// Owner has full server permissions // Owner has full server permissions
if ($server->owner_id === $user->id) { if ($server->owner_id === $user->id) {
return true; return true;

View File

@ -2,7 +2,7 @@
namespace App\Policies; namespace App\Policies;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\User; use App\Models\User;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -11,26 +11,26 @@ class SubuserPolicy
{ {
public function viewAny(User $user): bool public function viewAny(User $user): bool
{ {
return $user->can(Permission::ACTION_USER_READ, Filament::getTenant()); return $user->can(SubuserPermission::UserRead, Filament::getTenant());
} }
public function view(User $user, Model $record): bool public function view(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_USER_READ, Filament::getTenant()); return $user->can(SubuserPermission::UserRead, Filament::getTenant());
} }
public function create(User $user): bool public function create(User $user): bool
{ {
return $user->can(Permission::ACTION_USER_CREATE, Filament::getTenant()); return $user->can(SubuserPermission::UserCreate, Filament::getTenant());
} }
public function edit(User $user, Model $record): bool public function edit(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_USER_UPDATE, Filament::getTenant()); return $user->can(SubuserPermission::UserUpdate, Filament::getTenant());
} }
public function delete(User $user, Model $record): bool public function delete(User $user, Model $record): bool
{ {
return $user->can(Permission::ACTION_USER_DELETE, Filament::getTenant()); return $user->can(SubuserPermission::UserDelete, Filament::getTenant());
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Services\Servers; namespace App\Services\Servers;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser; use App\Models\Subuser;
use App\Models\User; use App\Models\User;
@ -32,7 +32,7 @@ class GetUserPermissionsService
]; ];
if ($isAdmin) { if ($isAdmin) {
return $isOwner || $user->can('update', $server) ? array_merge(['*'], $adminPermissions) : array_merge([Permission::ACTION_WEBSOCKET_CONNECT], $adminPermissions); return $isOwner || $user->can('update', $server) ? array_merge(['*'], $adminPermissions) : array_merge([SubuserPermission::WebsocketConnect->value], $adminPermissions);
} }
/** @var Subuser|null $subuser */ /** @var Subuser|null $subuser */

View File

@ -2,11 +2,11 @@
namespace App\Services\Subusers; namespace App\Services\Subusers;
use App\Enums\SubuserPermission;
use App\Events\Server\SubUserAdded; use App\Events\Server\SubUserAdded;
use App\Exceptions\Model\DataValidationException; use App\Exceptions\Model\DataValidationException;
use App\Exceptions\Service\Subuser\ServerSubuserExistsException; use App\Exceptions\Service\Subuser\ServerSubuserExistsException;
use App\Exceptions\Service\Subuser\UserIsServerOwnerException; use App\Exceptions\Service\Subuser\UserIsServerOwnerException;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser; use App\Models\Subuser;
use App\Models\User; use App\Models\User;
@ -58,7 +58,7 @@ class SubuserCreationService
$cleanedPermissions = collect($permissions) $cleanedPermissions = collect($permissions)
->unique() ->unique()
->filter(fn ($permission) => $permission === Permission::ACTION_WEBSOCKET_CONNECT || user()?->can($permission, $server)) ->filter(fn ($permission) => $permission === SubuserPermission::WebsocketConnect->value || user()?->can($permission, $server))
->sort() ->sort()
->values() ->values()
->all(); ->all();

View File

@ -2,8 +2,8 @@
namespace App\Services\Subusers; namespace App\Services\Subusers;
use App\Enums\SubuserPermission;
use App\Facades\Activity; use App\Facades\Activity;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser; use App\Models\Subuser;
use App\Repositories\Daemon\DaemonServerRepository; use App\Repositories\Daemon\DaemonServerRepository;
@ -22,7 +22,7 @@ class SubuserUpdateService
{ {
$cleanedPermissions = collect($permissions) $cleanedPermissions = collect($permissions)
->unique() ->unique()
->filter(fn ($permission) => $permission === Permission::ACTION_WEBSOCKET_CONNECT || user()?->can($permission, $server)) ->filter(fn ($permission) => $permission === SubuserPermission::WebsocketConnect->value || user()?->can($permission, $server))
->sort() ->sort()
->values() ->values()
->all(); ->all();

View File

@ -2,8 +2,8 @@
namespace App\Transformers\Api\Client; namespace App\Transformers\Api\Client;
use App\Enums\SubuserPermission;
use App\Models\Database; use App\Models\Database;
use App\Models\Permission;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Resource\NullResource; use League\Fractal\Resource\NullResource;
@ -41,7 +41,7 @@ class DatabaseTransformer extends BaseClientTransformer
*/ */
public function includePassword(Database $database): Item|NullResource public function includePassword(Database $database): Item|NullResource
{ {
if (!$this->request->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $database->server)) { if (!$this->request->user()->can(SubuserPermission::DatabaseViewPassword, $database->server)) {
return $this->null(); return $this->null();
} }

View File

@ -2,10 +2,10 @@
namespace App\Transformers\Api\Client; namespace App\Transformers\Api\Client;
use App\Enums\SubuserPermission;
use App\Models\Allocation; use App\Models\Allocation;
use App\Models\Egg; use App\Models\Egg;
use App\Models\EggVariable; use App\Models\EggVariable;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser; use App\Models\Subuser;
use App\Services\Servers\StartupCommandService; use App\Services\Servers\StartupCommandService;
@ -60,7 +60,7 @@ class ServerTransformer extends BaseClientTransformer
'oom_disabled' => !$server->oom_killer, 'oom_disabled' => !$server->oom_killer,
'oom_killer' => $server->oom_killer, 'oom_killer' => $server->oom_killer,
], ],
'invocation' => $service->handle($server, hideAllValues: !$user->can(Permission::ACTION_STARTUP_READ, $server)), 'invocation' => $service->handle($server, hideAllValues: !$user->can(SubuserPermission::StartupRead, $server)),
'docker_image' => $server->image, 'docker_image' => $server->image,
'egg_features' => $server->egg->inherit_features, 'egg_features' => $server->egg->inherit_features,
'feature_limits' => [ 'feature_limits' => [
@ -98,7 +98,7 @@ class ServerTransformer extends BaseClientTransformer
// //
// This allows us to avoid too much permission regression, without also hiding information that // This allows us to avoid too much permission regression, without also hiding information that
// is generally needed for the frontend to make sense when browsing or searching results. // is generally needed for the frontend to make sense when browsing or searching results.
if (!$user->can(Permission::ACTION_ALLOCATION_READ, $server)) { if (!$user->can(SubuserPermission::AllocationRead, $server)) {
$primary = clone $server->allocation; $primary = clone $server->allocation;
$primary->notes = null; $primary->notes = null;
@ -110,7 +110,7 @@ class ServerTransformer extends BaseClientTransformer
public function includeVariables(Server $server): Collection|NullResource public function includeVariables(Server $server): Collection|NullResource
{ {
if (!$this->request->user()->can(Permission::ACTION_STARTUP_READ, $server)) { if (!$this->request->user()->can(SubuserPermission::StartupRead, $server)) {
return $this->null(); return $this->null();
} }
@ -134,7 +134,7 @@ class ServerTransformer extends BaseClientTransformer
*/ */
public function includeSubusers(Server $server): Collection|NullResource public function includeSubusers(Server $server): Collection|NullResource
{ {
if (!$this->request->user()->can(Permission::ACTION_USER_READ, $server)) { if (!$this->request->user()->can(SubuserPermission::UserRead, $server)) {
return $this->null(); return $this->null();
} }

View File

@ -2,7 +2,6 @@
namespace Database\Factories; namespace Database\Factories;
use App\Models\Permission;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
class PermissionFactory extends Factory class PermissionFactory extends Factory

View File

@ -2,7 +2,7 @@
namespace Database\Factories; namespace Database\Factories;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\Subuser; use App\Models\Subuser;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
@ -22,7 +22,7 @@ class SubuserFactory extends Factory
{ {
return [ return [
'permissions' => [ 'permissions' => [
Permission::ACTION_WEBSOCKET_CONNECT, SubuserPermission::WebsocketConnect->value,
], ],
]; ];
} }

View File

@ -1,8 +1,8 @@
<?php <?php
use App\Enums\ServerState; use App\Enums\ServerState;
use App\Enums\SubuserPermission;
use App\Http\Controllers\Api\Client\Servers\SettingsController; use App\Http\Controllers\Api\Client\Servers\SettingsController;
use App\Models\Permission;
use App\Repositories\Daemon\DaemonServerRepository; use App\Repositories\Daemon\DaemonServerRepository;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -11,7 +11,7 @@ pest()->group('API');
covers(SettingsController::class); covers(SettingsController::class);
it('server name cannot be changed', function () { it('server name cannot be changed', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]); [$user, $server] = generateTestAccount([SubuserPermission::WebsocketConnect]);
$originalName = $server->name; $originalName = $server->name;
$this->actingAs($user) $this->actingAs($user)
@ -26,7 +26,7 @@ it('server name cannot be changed', function () {
}); });
it('server description can be changed', function () { it('server description can be changed', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_SETTINGS_DESCRIPTION]); [$user, $server] = generateTestAccount([SubuserPermission::SettingsDescription]);
$originalDescription = $server->description; $originalDescription = $server->description;
$newDescription = 'Test Server Description'; $newDescription = 'Test Server Description';
@ -45,7 +45,7 @@ it('server description can be changed', function () {
}); });
it('server description cannot be changed', function () { it('server description cannot be changed', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_SETTINGS_DESCRIPTION]); [$user, $server] = generateTestAccount([SubuserPermission::SettingsDescription]);
Config::set('panel.editable_server_descriptions', false); Config::set('panel.editable_server_descriptions', false);
$originalDescription = $server->description; $originalDescription = $server->description;
@ -61,7 +61,7 @@ it('server description cannot be changed', function () {
}); });
it('server name can be changed', function () { it('server name can be changed', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT, Permission::ACTION_SETTINGS_RENAME]); [$user, $server] = generateTestAccount([SubuserPermission::WebsocketConnect, SubuserPermission::SettingsRename]);
$originalName = $server->name; $originalName = $server->name;
$this->actingAs($user) $this->actingAs($user)
@ -76,7 +76,7 @@ it('server name can be changed', function () {
}); });
test('unauthorized user cannot change docker image in use by server', function () { test('unauthorized user cannot change docker image in use by server', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]); [$user, $server] = generateTestAccount([SubuserPermission::WebsocketConnect]);
$originalImage = $server->image; $originalImage = $server->image;
$this->actingAs($user) $this->actingAs($user)
@ -92,7 +92,7 @@ test('unauthorized user cannot change docker image in use by server', function (
test('cannot change docker image to image not allowed by egg', function () { test('cannot change docker image to image not allowed by egg', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_STARTUP_DOCKER_IMAGE]); [$user, $server] = generateTestAccount([SubuserPermission::StartupDockerImage]);
$server->image = 'ghcr.io/pelican-eggs/yolks:java_17'; $server->image = 'ghcr.io/pelican-eggs/yolks:java_17';
$server->save(); $server->save();
@ -112,7 +112,7 @@ test('cannot change docker image to image not allowed by egg', function () {
}); });
test('can change docker image in use by server', function () { test('can change docker image in use by server', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_STARTUP_DOCKER_IMAGE]); [$user, $server] = generateTestAccount([SubuserPermission::StartupDockerImage]);
$oldImage = 'ghcr.io/pelican-eggs/yolks:java_17'; $oldImage = 'ghcr.io/pelican-eggs/yolks:java_17';
$server->image = $oldImage; $server->image = $oldImage;
$server->save(); $server->save();
@ -135,7 +135,7 @@ test('can change docker image in use by server', function () {
}); });
test('unable to change the docker image set by administrator', function () { test('unable to change the docker image set by administrator', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_STARTUP_DOCKER_IMAGE]); [$user, $server] = generateTestAccount([SubuserPermission::StartupDockerImage]);
$oldImage = 'ghcr.io/pelican-eggs/yolks:java_custom'; $oldImage = 'ghcr.io/pelican-eggs/yolks:java_custom';
$server->image = $oldImage; $server->image = $oldImage;
$server->save(); $server->save();
@ -155,7 +155,7 @@ test('unable to change the docker image set by administrator', function () {
}); });
test('can be reinstalled', function () { test('can be reinstalled', function () {
[$user, $server] = generateTestAccount([Permission::ACTION_SETTINGS_REINSTALL]); [$user, $server] = generateTestAccount([SubuserPermission::SettingsReinstall]);
expect($server->isInstalled())->toBeTrue(); expect($server->isInstalled())->toBeTrue();
$service = \Mockery::mock(DaemonServerRepository::class); $service = \Mockery::mock(DaemonServerRepository::class);

View File

@ -3,7 +3,6 @@
use App\Enums\RolePermissionModels; use App\Enums\RolePermissionModels;
use App\Filament\Admin\Resources\Eggs\Pages\ListEggs; use App\Filament\Admin\Resources\Eggs\Pages\ListEggs;
use App\Models\Egg; use App\Models\Egg;
use App\Models\Permission;
use App\Models\Role; use App\Models\Role;
use function Pest\Livewire\livewire; use function Pest\Livewire\livewire;

View File

@ -3,7 +3,6 @@
use App\Enums\RolePermissionModels; use App\Enums\RolePermissionModels;
use App\Filament\Admin\Resources\Nodes\Pages\ListNodes; use App\Filament\Admin\Resources\Nodes\Pages\ListNodes;
use App\Models\Node; use App\Models\Node;
use App\Models\Permission;
use App\Models\Role; use App\Models\Role;
use App\Models\Server; use App\Models\Server;
use Filament\Actions\CreateAction; use Filament\Actions\CreateAction;

View File

@ -2,8 +2,8 @@
namespace App\Tests\Integration\Api\Client; namespace App\Tests\Integration\Api\Client;
use App\Enums\SubuserPermission;
use App\Models\Allocation; use App\Models\Allocation;
use App\Models\Permission;
use App\Models\Role; use App\Models\Role;
use App\Models\Server; use App\Models\Server;
use App\Models\Subuser; use App\Models\Subuser;
@ -158,7 +158,7 @@ class ClientControllerTest extends ClientApiIntegrationTestCase
Subuser::query()->create([ Subuser::query()->create([
'user_id' => $users[0]->id, 'user_id' => $users[0]->id,
'server_id' => $servers[1]->id, 'server_id' => $servers[1]->id,
'permissions' => [Permission::ACTION_WEBSOCKET_CONNECT], 'permissions' => [SubuserPermission::WebsocketConnect->value],
]); ]);
$response = $this->actingAs($users[0])->getJson('/api/client'); $response = $this->actingAs($users[0])->getJson('/api/client');
@ -189,7 +189,7 @@ class ClientControllerTest extends ClientApiIntegrationTestCase
Subuser::query()->create([ Subuser::query()->create([
'user_id' => $users[0]->id, 'user_id' => $users[0]->id,
'server_id' => $servers[1]->id, 'server_id' => $servers[1]->id,
'permissions' => [Permission::ACTION_WEBSOCKET_CONNECT], 'permissions' => [SubuserPermission::WebsocketConnect],
]); ]);
$response = $this->actingAs($users[0])->getJson('/api/client?type=owner'); $response = $this->actingAs($users[0])->getJson('/api/client?type=owner');
@ -214,7 +214,7 @@ class ClientControllerTest extends ClientApiIntegrationTestCase
->assertJson([ ->assertJson([
'object' => 'system_permissions', 'object' => 'system_permissions',
'attributes' => [ 'attributes' => [
'permissions' => Permission::permissions()->toArray(), 'permissions' => Subuser::allPermissionKeys(),
], ],
]); ]);
} }
@ -239,7 +239,7 @@ class ClientControllerTest extends ClientApiIntegrationTestCase
Subuser::query()->create([ Subuser::query()->create([
'user_id' => $users[0]->id, 'user_id' => $users[0]->id,
'server_id' => $servers[1]->id, 'server_id' => $servers[1]->id,
'permissions' => [Permission::ACTION_WEBSOCKET_CONNECT], 'permissions' => [SubuserPermission::WebsocketConnect->value],
]); ]);
// Only servers 2 & 3 (0 indexed) should be returned by the API at this point. The user making // Only servers 2 & 3 (0 indexed) should be returned by the API at this point. The user making
@ -274,7 +274,7 @@ class ClientControllerTest extends ClientApiIntegrationTestCase
Subuser::query()->create([ Subuser::query()->create([
'user_id' => $users[0]->id, 'user_id' => $users[0]->id,
'server_id' => $servers[1]->id, 'server_id' => $servers[1]->id,
'permissions' => [Permission::ACTION_WEBSOCKET_CONNECT], 'permissions' => [SubuserPermission::WebsocketConnect->value],
]); ]);
// All servers should be returned. // All servers should be returned.
@ -311,7 +311,7 @@ class ClientControllerTest extends ClientApiIntegrationTestCase
public function test_only_primary_allocation_is_returned_to_subuser(): void public function test_only_primary_allocation_is_returned_to_subuser(): void
{ {
/** @var \App\Models\Server $server */ /** @var \App\Models\Server $server */
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]); [$user, $server] = $this->generateTestAccount([SubuserPermission::WebsocketConnect]);
$server->allocation->notes = 'Test notes'; $server->allocation->notes = 'Test notes';
$server->allocation->save(); $server->allocation->save();

View File

@ -2,8 +2,8 @@
namespace App\Tests\Integration\Api\Client\Server\Allocation; namespace App\Tests\Integration\Api\Client\Server\Allocation;
use App\Enums\SubuserPermission;
use App\Models\Allocation; use App\Models\Allocation;
use App\Models\Permission;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\DataProvider;
@ -48,7 +48,7 @@ class CreateNewAllocationTest extends ClientApiIntegrationTestCase
public function test_allocation_cannot_be_created_if_user_does_not_have_permission(): void public function test_allocation_cannot_be_created_if_user_does_not_have_permission(): void
{ {
/** @var \App\Models\Server $server */ /** @var \App\Models\Server $server */
[$user, $server] = $this->generateTestAccount([Permission::ACTION_ALLOCATION_UPDATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::AllocationUpdate]);
$server->update(['allocation_limit' => 2]); $server->update(['allocation_limit' => 2]);
$this->actingAs($user)->postJson($this->link($server, '/network/allocations'))->assertForbidden(); $this->actingAs($user)->postJson($this->link($server, '/network/allocations'))->assertForbidden();
@ -88,6 +88,6 @@ class CreateNewAllocationTest extends ClientApiIntegrationTestCase
public static function permissionDataProvider(): array public static function permissionDataProvider(): array
{ {
return [[[Permission::ACTION_ALLOCATION_CREATE]], [[]]]; return [[[SubuserPermission::AllocationCreate]], [[]]];
} }
} }

View File

@ -2,8 +2,8 @@
namespace App\Tests\Integration\Api\Client\Server\Allocation; namespace App\Tests\Integration\Api\Client\Server\Allocation;
use App\Enums\SubuserPermission;
use App\Models\Allocation; use App\Models\Allocation;
use App\Models\Permission;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\DataProvider;
@ -56,7 +56,7 @@ class DeleteAllocationTest extends ClientApiIntegrationTestCase
public function test_error_is_returned_if_user_does_not_have_permission(): void public function test_error_is_returned_if_user_does_not_have_permission(): void
{ {
/** @var \App\Models\Server $server */ /** @var \App\Models\Server $server */
[$user, $server] = $this->generateTestAccount([Permission::ACTION_ALLOCATION_CREATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::AllocationCreate]);
/** @var \App\Models\Allocation $allocation */ /** @var \App\Models\Allocation $allocation */
$allocation = Allocation::factory()->create([ $allocation = Allocation::factory()->create([
@ -101,6 +101,6 @@ class DeleteAllocationTest extends ClientApiIntegrationTestCase
public static function permissionDataProvider(): array public static function permissionDataProvider(): array
{ {
return [[[Permission::ACTION_ALLOCATION_DELETE]], [[]]]; return [[[SubuserPermission::AllocationDelete]], [[]]];
} }
} }

View File

@ -2,9 +2,9 @@
namespace App\Tests\Integration\Api\Client\Server\Backup; namespace App\Tests\Integration\Api\Client\Server\Backup;
use App\Enums\SubuserPermission;
use App\Events\ActivityLogged; use App\Events\ActivityLogged;
use App\Models\Backup; use App\Models\Backup;
use App\Models\Permission;
use App\Repositories\Daemon\DaemonBackupRepository; use App\Repositories\Daemon\DaemonBackupRepository;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -24,7 +24,7 @@ class DeleteBackupTest extends ClientApiIntegrationTestCase
public function test_user_without_permission_cannot_delete_backup(): void public function test_user_without_permission_cannot_delete_backup(): void
{ {
[$user, $server] = $this->generateTestAccount([Permission::ACTION_BACKUP_CREATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::BackupCreate]);
$backup = Backup::factory()->create(['server_id' => $server->id]); $backup = Backup::factory()->create(['server_id' => $server->id]);
@ -41,7 +41,7 @@ class DeleteBackupTest extends ClientApiIntegrationTestCase
{ {
Event::fake([ActivityLogged::class]); Event::fake([ActivityLogged::class]);
[$user, $server] = $this->generateTestAccount([Permission::ACTION_BACKUP_DELETE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::BackupDelete]);
/** @var \App\Models\Backup $backup */ /** @var \App\Models\Backup $backup */
$backup = Backup::factory()->create(['server_id' => $server->id]); $backup = Backup::factory()->create(['server_id' => $server->id]);

View File

@ -2,9 +2,9 @@
namespace App\Tests\Integration\Api\Client\Server; namespace App\Tests\Integration\Api\Client\Server;
use App\Enums\SubuserPermission;
use App\Http\Controllers\Api\Client\Servers\CommandController; use App\Http\Controllers\Api\Client\Servers\CommandController;
use App\Http\Requests\Api\Client\Servers\SendCommandRequest; use App\Http\Requests\Api\Client\Servers\SendCommandRequest;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
use GuzzleHttp\Exception\BadResponseException; use GuzzleHttp\Exception\BadResponseException;
@ -38,7 +38,7 @@ class CommandControllerTest extends ClientApiIntegrationTestCase
*/ */
public function test_subuser_without_permission_receives_error(): void public function test_subuser_without_permission_receives_error(): void
{ {
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]); [$user, $server] = $this->generateTestAccount([SubuserPermission::WebsocketConnect]);
$response = $this->actingAs($user)->postJson("/api/client/servers/$server->uuid/command", [ $response = $this->actingAs($user)->postJson("/api/client/servers/$server->uuid/command", [
'command' => 'say Test', 'command' => 'say Test',
@ -52,7 +52,7 @@ class CommandControllerTest extends ClientApiIntegrationTestCase
*/ */
public function test_command_can_send_to_server(): void public function test_command_can_send_to_server(): void
{ {
[$user, $server] = $this->generateTestAccount([Permission::ACTION_CONTROL_CONSOLE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::ControlConsole]);
$server = \Mockery::mock($server)->makePartial(); $server = \Mockery::mock($server)->makePartial();

View File

@ -2,8 +2,8 @@
namespace App\Tests\Integration\Api\Client\Server; namespace App\Tests\Integration\Api\Client\Server;
use App\Enums\SubuserPermission;
use App\Models\Allocation; use App\Models\Allocation;
use App\Models\Permission;
use App\Models\User; use App\Models\User;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -41,7 +41,7 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
$this->actingAs($user)->getJson($this->link($server, '/network/allocations')) $this->actingAs($user)->getJson($this->link($server, '/network/allocations'))
->assertNotFound(); ->assertNotFound();
[$user, $server] = $this->generateTestAccount([Permission::ACTION_ALLOCATION_CREATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::AllocationCreate]);
$this->actingAs($user)->getJson($this->link($server, '/network/allocations')) $this->actingAs($user)->getJson($this->link($server, '/network/allocations'))
->assertForbidden(); ->assertForbidden();
@ -91,7 +91,7 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
$this->actingAs($user)->postJson($this->link($server->allocation))->assertNotFound(); $this->actingAs($user)->postJson($this->link($server->allocation))->assertNotFound();
[$user, $server] = $this->generateTestAccount([Permission::ACTION_ALLOCATION_CREATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::AllocationCreate]);
$this->actingAs($user)->postJson($this->link($server->allocation))->assertForbidden(); $this->actingAs($user)->postJson($this->link($server->allocation))->assertForbidden();
} }
@ -125,7 +125,7 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
$this->actingAs($user)->postJson($this->link($server->allocation, '/primary')) $this->actingAs($user)->postJson($this->link($server->allocation, '/primary'))
->assertNotFound(); ->assertNotFound();
[$user, $server] = $this->generateTestAccount([Permission::ACTION_ALLOCATION_CREATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::AllocationCreate]);
$this->actingAs($user)->postJson($this->link($server->allocation, '/primary')) $this->actingAs($user)->postJson($this->link($server->allocation, '/primary'))
->assertForbidden(); ->assertForbidden();
@ -133,6 +133,6 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
public static function updatePermissionsDataProvider(): array public static function updatePermissionsDataProvider(): array
{ {
return [[[]], [[Permission::ACTION_ALLOCATION_UPDATE]]]; return [[[]], [[SubuserPermission::AllocationUpdate]]];
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Tests\Integration\Api\Client\Server; namespace App\Tests\Integration\Api\Client\Server;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Repositories\Daemon\DaemonServerRepository; use App\Repositories\Daemon\DaemonServerRepository;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -15,7 +15,7 @@ class PowerControllerTest extends ClientApiIntegrationTestCase
* an error in response. This checks against the specific permission needed to send * an error in response. This checks against the specific permission needed to send
* the command to the server. * the command to the server.
* *
* @param string[] $permissions * @param array<string|SubuserPermission> $permissions
*/ */
#[DataProvider('invalidPermissionDataProvider')] #[DataProvider('invalidPermissionDataProvider')]
public function test_subuser_without_permissions_receives_error(string $action, array $permissions): void public function test_subuser_without_permissions_receives_error(string $action, array $permissions): void
@ -47,7 +47,7 @@ class PowerControllerTest extends ClientApiIntegrationTestCase
* Test that sending a valid power actions works. * Test that sending a valid power actions works.
*/ */
#[DataProvider('validPowerActionDataProvider')] #[DataProvider('validPowerActionDataProvider')]
public function test_action_can_be_sent_to_server(string $action, string $permission): void public function test_action_can_be_sent_to_server(string $action, string|SubuserPermission $permission): void
{ {
$service = \Mockery::mock(DaemonServerRepository::class); $service = \Mockery::mock(DaemonServerRepository::class);
$this->app->instance(DaemonServerRepository::class, $service); $this->app->instance(DaemonServerRepository::class, $service);
@ -74,25 +74,25 @@ class PowerControllerTest extends ClientApiIntegrationTestCase
public static function invalidPermissionDataProvider(): array public static function invalidPermissionDataProvider(): array
{ {
return [ return [
['start', [Permission::ACTION_CONTROL_STOP, Permission::ACTION_CONTROL_RESTART]], ['start', [SubuserPermission::ControlStop, SubuserPermission::ControlRestart]],
['stop', [Permission::ACTION_CONTROL_START]], ['stop', [SubuserPermission::ControlStart]],
['kill', [Permission::ACTION_CONTROL_START, Permission::ACTION_CONTROL_RESTART]], ['kill', [SubuserPermission::ControlStart, SubuserPermission::ControlRestart]],
['restart', [Permission::ACTION_CONTROL_STOP, Permission::ACTION_CONTROL_START]], ['restart', [SubuserPermission::ControlStop, SubuserPermission::ControlStart]],
['random', [Permission::ACTION_CONTROL_START]], ['random', [SubuserPermission::ControlStart]],
]; ];
} }
public static function validPowerActionDataProvider(): array public static function validPowerActionDataProvider(): array
{ {
return [ return [
['start', Permission::ACTION_CONTROL_START], ['start', SubuserPermission::ControlStart],
['stop', Permission::ACTION_CONTROL_STOP], ['stop', SubuserPermission::ControlStop],
['restart', Permission::ACTION_CONTROL_RESTART], ['restart', SubuserPermission::ControlRestart],
['kill', Permission::ACTION_CONTROL_STOP], ['kill', SubuserPermission::ControlStop],
// Yes, these spaces are intentional. You should be able to send values with or without // Yes, these spaces are intentional. You should be able to send values with or without
// a space on the start/end since we should be trimming the values. // a space on the start/end since we should be trimming the values.
[' restart', Permission::ACTION_CONTROL_RESTART], [' restart', SubuserPermission::ControlRestart],
['kill ', Permission::ACTION_CONTROL_STOP], ['kill ', SubuserPermission::ControlStop],
]; ];
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Tests\Integration\Api\Client\Server; namespace App\Tests\Integration\Api\Client\Server;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Repositories\Daemon\DaemonServerRepository; use App\Repositories\Daemon\DaemonServerRepository;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
@ -16,7 +16,7 @@ class ResourceUtilizationControllerTest extends ClientApiIntegrationTestCase
$service = \Mockery::mock(DaemonServerRepository::class); $service = \Mockery::mock(DaemonServerRepository::class);
$this->app->instance(DaemonServerRepository::class, $service); $this->app->instance(DaemonServerRepository::class, $service);
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]); [$user, $server] = $this->generateTestAccount([SubuserPermission::WebsocketConnect]);
$service->expects('setServer')->with(\Mockery::on(function ($value) use ($server) { $service->expects('setServer')->with(\Mockery::on(function ($value) use ($server) {
return $server->uuid === $value->uuid; return $server->uuid === $value->uuid;

View File

@ -2,7 +2,7 @@
namespace App\Tests\Integration\Api\Client\Server\Schedule; namespace App\Tests\Integration\Api\Client\Server\Schedule;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\Schedule; use App\Models\Schedule;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -83,7 +83,7 @@ class CreateServerScheduleTest extends ClientApiIntegrationTestCase
*/ */
public function test_subuser_cannot_create_schedule_without_permissions(): void public function test_subuser_cannot_create_schedule_without_permissions(): void
{ {
[$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_UPDATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::ScheduleUpdate]);
$this->actingAs($user) $this->actingAs($user)
->postJson("/api/client/servers/$server->uuid/schedules", []) ->postJson("/api/client/servers/$server->uuid/schedules", [])
@ -92,6 +92,6 @@ class CreateServerScheduleTest extends ClientApiIntegrationTestCase
public static function permissionsDataProvider(): array public static function permissionsDataProvider(): array
{ {
return [[[]], [[Permission::ACTION_SCHEDULE_CREATE]]]; return [[[]], [[SubuserPermission::ScheduleCreate]]];
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Tests\Integration\Api\Client\Server\Schedule; namespace App\Tests\Integration\Api\Client\Server\Schedule;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\Schedule; use App\Models\Schedule;
use App\Models\Task; use App\Models\Task;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
@ -66,7 +66,7 @@ class DeleteServerScheduleTest extends ClientApiIntegrationTestCase
*/ */
public function test_error_is_returned_if_subuser_does_not_have_required_permissions(): void public function test_error_is_returned_if_subuser_does_not_have_required_permissions(): void
{ {
[$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_UPDATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::ScheduleUpdate]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]); $schedule = Schedule::factory()->create(['server_id' => $server->id]);
@ -79,6 +79,6 @@ class DeleteServerScheduleTest extends ClientApiIntegrationTestCase
public static function permissionsDataProvider(): array public static function permissionsDataProvider(): array
{ {
return [[[]], [[Permission::ACTION_SCHEDULE_DELETE]]]; return [[[]], [[SubuserPermission::ScheduleDelete]]];
} }
} }

View File

@ -2,8 +2,8 @@
namespace App\Tests\Integration\Api\Client\Server\Schedule; namespace App\Tests\Integration\Api\Client\Server\Schedule;
use App\Enums\SubuserPermission;
use App\Jobs\Schedule\RunTaskJob; use App\Jobs\Schedule\RunTaskJob;
use App\Models\Permission;
use App\Models\Schedule; use App\Models\Schedule;
use App\Models\Task; use App\Models\Task;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
@ -56,7 +56,7 @@ class ExecuteScheduleTest extends ClientApiIntegrationTestCase
*/ */
public function test_user_without_schedule_update_permission_cannot_execute(): void public function test_user_without_schedule_update_permission_cannot_execute(): void
{ {
[$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_CREATE]); [$user, $server] = $this->generateTestAccount([SubuserPermission::ScheduleCreate]);
/** @var \App\Models\Schedule $schedule */ /** @var \App\Models\Schedule $schedule */
$schedule = Schedule::factory()->create(['server_id' => $server->id]); $schedule = Schedule::factory()->create(['server_id' => $server->id]);
@ -66,6 +66,6 @@ class ExecuteScheduleTest extends ClientApiIntegrationTestCase
public static function permissionsDataProvider(): array public static function permissionsDataProvider(): array
{ {
return [[[]], [[Permission::ACTION_SCHEDULE_UPDATE]]]; return [[[]], [[SubuserPermission::ScheduleUpdate]]];
} }
} }

View File

@ -2,7 +2,7 @@
namespace App\Tests\Integration\Api\Client\Server\Schedule; namespace App\Tests\Integration\Api\Client\Server\Schedule;
use App\Models\Permission; use App\Enums\SubuserPermission;
use App\Models\Schedule; use App\Models\Schedule;
use App\Models\Task; use App\Models\Task;
use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
@ -76,7 +76,7 @@ class GetServerSchedulesTest extends ClientApiIntegrationTestCase
*/ */
public function test_user_without_permission_cannot_view_schedules(): void public function test_user_without_permission_cannot_view_schedules(): void
{ {
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]); [$user, $server] = $this->generateTestAccount([SubuserPermission::WebsocketConnect]);
$this->actingAs($user) $this->actingAs($user)
->getJson("/api/client/servers/$server->uuid/schedules") ->getJson("/api/client/servers/$server->uuid/schedules")
@ -94,8 +94,8 @@ class GetServerSchedulesTest extends ClientApiIntegrationTestCase
return [ return [
[[], false], [[], false],
[[], true], [[], true],
[[Permission::ACTION_SCHEDULE_READ], false], [[SubuserPermission::ScheduleRead], false],
[[Permission::ACTION_SCHEDULE_READ], true], [[SubuserPermission::ScheduleRead], true],
]; ];
} }
} }

Some files were not shown because too many files have changed in this diff Show More