From 63fcc0ebc3a41a91eb4a48265d9d637f80afcf31 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:27:31 +0200 Subject: [PATCH] feat(acl): Change views/backend code to able to use proper ACL's later on. Currently it is not enabled. --- app/Http/Kernel.php | 2 + app/Http/Middleware/CanAccessTerminal.php | 31 +++ app/Http/Middleware/CanCreateResources.php | 9 +- app/Http/Middleware/CanUpdateResource.php | 75 +++++ app/Livewire/Destination/Show.php | 7 + app/Livewire/Notifications/Discord.php | 6 + app/Livewire/Notifications/Email.php | 7 + app/Livewire/Notifications/Pushover.php | 6 + app/Livewire/Notifications/Slack.php | 6 + app/Livewire/Notifications/Telegram.php | 6 + .../Project/Application/Preview/Form.php | 5 + app/Livewire/Project/Application/Previews.php | 1 + app/Livewire/Project/Database/Import.php | 1 + app/Livewire/Project/Index.php | 1 + .../Project/Service/Configuration.php | 45 +-- app/Livewire/Project/Service/Database.php | 102 ++++--- app/Livewire/Project/Service/FileStorage.php | 13 + app/Livewire/Project/Service/Index.php | 11 +- .../Service/ServiceApplicationView.php | 54 ++-- app/Livewire/Project/Service/Storage.php | 5 + .../Shared/EnvironmentVariable/Add.php | 3 + .../Shared/EnvironmentVariable/Show.php | 18 +- app/Livewire/Project/Shared/HealthChecks.php | 5 + .../Project/Shared/ResourceLimits.php | 4 + .../Project/Shared/ScheduledTask/Add.php | 23 ++ .../Project/Shared/ScheduledTask/Show.php | 7 + app/Livewire/Project/Shared/Storages/Add.php | 9 + app/Livewire/Project/Shared/Storages/Show.php | 9 + app/Livewire/Project/Shared/Tags.php | 6 + app/Livewire/Project/Shared/Webhooks.php | 3 + app/Livewire/Security/ApiTokens.php | 39 +++ app/Livewire/Security/PrivateKey/Index.php | 4 + .../Proxy/DynamicConfigurationNavbar.php | 15 +- .../SharedVariables/Environment/Show.php | 5 + app/Livewire/SharedVariables/Project/Show.php | 5 + app/Livewire/SharedVariables/Team/Index.php | 5 + app/Livewire/Source/Github/Change.php | 19 +- app/Livewire/Source/Github/Create.php | 5 + app/Livewire/Storage/Create.php | 5 + app/Livewire/Storage/Form.php | 9 +- app/Livewire/Team/Index.php | 5 + app/Livewire/Team/Invitations.php | 5 + app/Livewire/Team/InviteLink.php | 4 + app/Livewire/Team/Member.php | 11 + app/Livewire/Team/Member/Index.php | 6 +- app/Livewire/Terminal/Index.php | 1 - app/Models/SharedEnvironmentVariable.php | 15 + app/Policies/ApiTokenPolicy.php | 109 ++++++++ app/Policies/ApplicationPolicy.php | 52 +++- app/Policies/ApplicationPreviewPolicy.php | 30 +- app/Policies/ApplicationSettingPolicy.php | 18 +- app/Policies/DatabasePolicy.php | 33 ++- app/Policies/EnvironmentPolicy.php | 18 +- app/Policies/GithubAppPolicy.php | 79 ++++++ app/Policies/NotificationPolicy.php | 56 ++++ app/Policies/PrivateKeyPolicy.php | 12 +- app/Policies/ProjectPolicy.php | 18 +- app/Policies/ResourceCreatePolicy.php | 7 +- app/Policies/S3StoragePolicy.php | 19 +- app/Policies/ServerPolicy.php | 16 +- app/Policies/ServiceApplicationPolicy.php | 63 +++++ app/Policies/ServiceDatabasePolicy.php | 63 +++++ app/Policies/ServicePolicy.php | 60 +++- .../SharedEnvironmentVariablePolicy.php | 79 ++++++ app/Policies/StandaloneDockerPolicy.php | 17 +- app/Policies/SwarmDockerPolicy.php | 17 +- app/Policies/TeamPolicy.php | 104 +++++++ app/Providers/AuthServiceProvider.php | 25 ++ app/View/Components/Forms/Button.php | 13 + app/View/Components/Forms/Checkbox.php | 14 + app/View/Components/Forms/Input.php | 15 +- app/View/Components/Forms/Select.php | 15 +- app/View/Components/Forms/Textarea.php | 13 +- resources/views/components/navbar.blade.php | 58 ++-- .../livewire/destination/index.blade.php | 8 +- .../livewire/destination/new/docker.blade.php | 43 +-- .../views/livewire/destination/show.blade.php | 10 +- .../livewire/notifications/discord.blade.php | 38 +-- .../livewire/notifications/email.blade.php | 92 ++++--- .../livewire/notifications/pushover.blade.php | 38 +-- .../livewire/notifications/slack.blade.php | 36 +-- .../livewire/notifications/telegram.blade.php | 64 ++--- .../project/application/advanced.blade.php | 78 +++--- .../project/application/general.blade.php | 257 +++++++++--------- .../project/application/heading.blade.php | 10 +- .../application/preview/form.blade.php | 8 +- .../application/previews-compose.blade.php | 2 +- .../project/application/previews.blade.php | 184 +++++++------ .../project/application/rollback.blade.php | 26 +- .../project/application/source.blade.php | 90 +++--- .../project/application/swarm.blade.php | 6 +- .../project/database/backup/index.blade.php | 8 +- .../database/clickhouse/general.blade.php | 37 +-- .../project/database/configuration.blade.php | 8 +- .../database/dragonfly/general.blade.php | 38 +-- .../project/database/heading.blade.php | 10 +- .../project/database/import.blade.php | 4 +- .../project/database/keydb/general.blade.php | 40 +-- .../database/mariadb/general.blade.php | 52 ++-- .../database/mongodb/general.blade.php | 56 ++-- .../project/database/mysql/general.blade.php | 38 +-- .../database/postgresql/general.blade.php | 75 ++--- .../project/database/redis/general.blade.php | 47 ++-- .../database/scheduled-backups.blade.php | 4 +- .../project/environment-edit.blade.php | 11 +- .../views/livewire/project/index.blade.php | 12 +- .../project/service/configuration.blade.php | 76 +++--- .../project/service/database.blade.php | 38 +-- .../project/service/edit-domain.blade.php | 4 +- .../project/service/file-storage.blade.php | 100 ++++--- .../project/service/heading.blade.php | 10 +- .../livewire/project/service/index.blade.php | 8 +- .../service-application-view.blade.php | 52 ++-- .../project/service/stack-form.blade.php | 18 +- .../project/service/storage.blade.php | 8 +- .../shared/environment-variable/all.blade.php | 55 ++-- .../environment-variable/show.blade.php | 198 +++++++++----- .../project/shared/health-checks.blade.php | 28 +- .../project/shared/resource-limits.blade.php | 17 +- .../shared/resource-operations.blade.php | 99 ++++--- .../shared/scheduled-task/all.blade.php | 16 +- .../project/shared/storages/all.blade.php | 4 +- .../project/shared/storages/show.blade.php | 74 +++-- .../livewire/project/shared/tags.blade.php | 59 ++-- .../project/shared/webhooks.blade.php | 53 +++- .../views/livewire/project/show.blade.php | 33 +-- .../livewire/security/api-tokens.blade.php | 104 ++++--- .../security/private-key/index.blade.php | 14 +- .../security/private-key/show.blade.php | 34 +-- .../views/livewire/server/advanced.blade.php | 14 +- .../server/ca-certificate/show.blade.php | 54 ++-- .../server/cloudflare-tunnel.blade.php | 44 ++- .../livewire/server/destinations.blade.php | 12 +- .../livewire/server/docker-cleanup.blade.php | 39 +-- .../views/livewire/server/index.blade.php | 8 +- .../livewire/server/log-drains.blade.php | 37 +-- .../views/livewire/server/navbar.blade.php | 28 +- .../server/private-key/show.blade.php | 12 +- .../views/livewire/server/proxy.blade.php | 68 +++-- .../dynamic-configuration-navbar.blade.php | 16 +- .../proxy/dynamic-configurations.blade.php | 10 +- .../proxy/new-dynamic-configuration.blade.php | 7 +- .../views/livewire/server/show.blade.php | 172 +++++++----- .../environment/index.blade.php | 4 +- .../environment/show.blade.php | 8 +- .../shared-variables/project/show.blade.php | 10 +- .../shared-variables/team/index.blade.php | 8 +- .../livewire/source/github/change.blade.php | 255 +++++++++-------- .../livewire/source/github/create.blade.php | 99 +++---- .../views/livewire/storage/create.blade.php | 53 ++-- .../views/livewire/storage/form.blade.php | 44 +-- .../views/livewire/storage/index.blade.php | 8 +- resources/views/livewire/team/index.blade.php | 124 +++++---- .../views/livewire/team/invitations.blade.php | 108 ++++---- .../views/livewire/team/invite-link.blade.php | 36 +-- .../views/livewire/team/member.blade.php | 8 +- .../livewire/team/member/index.blade.php | 4 +- resources/views/source/all.blade.php | 20 +- routes/web.php | 29 +- 159 files changed, 3610 insertions(+), 1922 deletions(-) create mode 100644 app/Http/Middleware/CanAccessTerminal.php create mode 100644 app/Http/Middleware/CanUpdateResource.php create mode 100644 app/Policies/ApiTokenPolicy.php create mode 100644 app/Policies/GithubAppPolicy.php create mode 100644 app/Policies/NotificationPolicy.php create mode 100644 app/Policies/ServiceApplicationPolicy.php create mode 100644 app/Policies/ServiceDatabasePolicy.php create mode 100644 app/Policies/SharedEnvironmentVariablePolicy.php create mode 100644 app/Policies/TeamPolicy.php diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 465d15470..e9d7b82b2 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -72,5 +72,7 @@ class Kernel extends HttpKernel 'api.ability' => \App\Http\Middleware\ApiAbility::class, 'api.sensitive' => \App\Http\Middleware\ApiSensitiveData::class, 'can.create.resources' => \App\Http\Middleware\CanCreateResources::class, + 'can.update.resource' => \App\Http\Middleware\CanUpdateResource::class, + 'can.access.terminal' => \App\Http\Middleware\CanAccessTerminal::class, ]; } diff --git a/app/Http/Middleware/CanAccessTerminal.php b/app/Http/Middleware/CanAccessTerminal.php new file mode 100644 index 000000000..dcccd819b --- /dev/null +++ b/app/Http/Middleware/CanAccessTerminal.php @@ -0,0 +1,31 @@ +check()) { + // abort(401, 'Authentication required'); + // } + + // // Only admins/owners can access terminal functionality + // if (! auth()->user()->can('canAccessTerminal')) { + // abort(403, 'Access to terminal functionality is restricted to team administrators'); + // } + + // return $next($request); + } +} diff --git a/app/Http/Middleware/CanCreateResources.php b/app/Http/Middleware/CanCreateResources.php index e91118ab0..ba0ab67c1 100644 --- a/app/Http/Middleware/CanCreateResources.php +++ b/app/Http/Middleware/CanCreateResources.php @@ -16,10 +16,11 @@ class CanCreateResources */ public function handle(Request $request, Closure $next): Response { - if (! Gate::allows('createAnyResource')) { - abort(403, 'You do not have permission to create resources.'); - } - return $next($request); + // if (! Gate::allows('createAnyResource')) { + // abort(403, 'You do not have permission to create resources.'); + // } + + // return $next($request); } } diff --git a/app/Http/Middleware/CanUpdateResource.php b/app/Http/Middleware/CanUpdateResource.php new file mode 100644 index 000000000..372af4498 --- /dev/null +++ b/app/Http/Middleware/CanUpdateResource.php @@ -0,0 +1,75 @@ +route('application_uuid')) { + // $resource = Application::where('uuid', $request->route('application_uuid'))->first(); + // } elseif ($request->route('service_uuid')) { + // $resource = Service::where('uuid', $request->route('service_uuid'))->first(); + // } elseif ($request->route('stack_service_uuid')) { + // // Handle ServiceApplication or ServiceDatabase + // $stack_service_uuid = $request->route('stack_service_uuid'); + // $resource = ServiceApplication::where('uuid', $stack_service_uuid)->first() ?? + // ServiceDatabase::where('uuid', $stack_service_uuid)->first(); + // } elseif ($request->route('database_uuid')) { + // // Try different database types + // $database_uuid = $request->route('database_uuid'); + // $resource = StandalonePostgresql::where('uuid', $database_uuid)->first() ?? + // StandaloneMysql::where('uuid', $database_uuid)->first() ?? + // StandaloneMariadb::where('uuid', $database_uuid)->first() ?? + // StandaloneRedis::where('uuid', $database_uuid)->first() ?? + // StandaloneKeydb::where('uuid', $database_uuid)->first() ?? + // StandaloneDragonfly::where('uuid', $database_uuid)->first() ?? + // StandaloneClickhouse::where('uuid', $database_uuid)->first() ?? + // StandaloneMongodb::where('uuid', $database_uuid)->first(); + // } elseif ($request->route('server_uuid')) { + // // For server routes, check if user can manage servers + // if (! auth()->user()->isAdmin()) { + // abort(403, 'You do not have permission to access this resource.'); + // } + + // return $next($request); + // } elseif ($request->route('environment_uuid')) { + // $resource = Environment::where('uuid', $request->route('environment_uuid'))->first(); + // } elseif ($request->route('project_uuid')) { + // $resource = Project::ownedByCurrentTeam()->where('uuid', $request->route('project_uuid'))->first(); + // } + + // if (! $resource) { + // abort(404, 'Resource not found.'); + // } + + // if (! Gate::allows('update', $resource)) { + // abort(403, 'You do not have permission to update this resource.'); + // } + + // return $next($request); + } +} diff --git a/app/Livewire/Destination/Show.php b/app/Livewire/Destination/Show.php index 5c4d6c170..98cf72376 100644 --- a/app/Livewire/Destination/Show.php +++ b/app/Livewire/Destination/Show.php @@ -5,12 +5,15 @@ use App\Models\Server; use App\Models\StandaloneDocker; use App\Models\SwarmDocker; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; class Show extends Component { + use AuthorizesRequests; + #[Locked] public $destination; @@ -63,6 +66,8 @@ public function syncData(bool $toModel = false) public function submit() { try { + $this->authorize('update', $this->destination); + $this->syncData(true); $this->dispatch('success', 'Destination saved.'); } catch (\Throwable $e) { @@ -73,6 +78,8 @@ public function submit() public function delete() { try { + $this->authorize('delete', $this->destination); + if ($this->destination->getMorphClass() === \App\Models\StandaloneDocker::class) { if ($this->destination->attachedTo()) { return $this->dispatch('error', 'You must delete all resources before deleting this destination.'); diff --git a/app/Livewire/Notifications/Discord.php b/app/Livewire/Notifications/Discord.php index e0425fa17..28d1cb866 100644 --- a/app/Livewire/Notifications/Discord.php +++ b/app/Livewire/Notifications/Discord.php @@ -5,11 +5,14 @@ use App\Models\DiscordNotificationSettings; use App\Models\Team; use App\Notifications\Test; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Attributes\Validate; use Livewire\Component; class Discord extends Component { + use AuthorizesRequests; + public Team $team; public DiscordNotificationSettings $settings; @@ -67,6 +70,7 @@ public function mount() try { $this->team = auth()->user()->currentTeam(); $this->settings = $this->team->discordNotificationSettings; + $this->authorize('view', $this->settings); $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); @@ -77,6 +81,7 @@ public function syncData(bool $toModel = false) { if ($toModel) { $this->validate(); + $this->authorize('update', $this->settings); $this->settings->discord_enabled = $this->discordEnabled; $this->settings->discord_webhook_url = $this->discordWebhookUrl; @@ -182,6 +187,7 @@ public function saveModel() public function sendTestNotification() { try { + $this->authorize('sendTest', $this->settings); $this->team->notify(new Test(channel: 'discord')); $this->dispatch('success', 'Test notification sent.'); } catch (\Throwable $e) { diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php index 128321ed2..d62a08417 100644 --- a/app/Livewire/Notifications/Email.php +++ b/app/Livewire/Notifications/Email.php @@ -5,6 +5,7 @@ use App\Models\EmailNotificationSettings; use App\Models\Team; use App\Notifications\Test; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\RateLimiter; use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; @@ -12,6 +13,8 @@ class Email extends Component { + use AuthorizesRequests; + protected $listeners = ['refresh' => '$refresh']; #[Locked] @@ -110,6 +113,7 @@ public function mount() $this->team = auth()->user()->currentTeam(); $this->emails = auth()->user()->email; $this->settings = $this->team->emailNotificationSettings; + $this->authorize('view', $this->settings); $this->syncData(); $this->testEmailAddress = auth()->user()->email; } catch (\Throwable $e) { @@ -121,6 +125,7 @@ public function syncData(bool $toModel = false) { if ($toModel) { $this->validate(); + $this->authorize('update', $this->settings); $this->settings->smtp_enabled = $this->smtpEnabled; $this->settings->smtp_from_address = $this->smtpFromAddress; $this->settings->smtp_from_name = $this->smtpFromName; @@ -311,6 +316,7 @@ public function submitResend() public function sendTestEmail() { try { + $this->authorize('sendTest', $this->settings); $this->validate([ 'testEmailAddress' => 'required|email', ], [ @@ -338,6 +344,7 @@ function () { public function copyFromInstanceSettings() { + $this->authorize('update', $this->settings); $settings = instanceSettings(); $this->smtpFromAddress = $settings->smtp_from_address; $this->smtpFromName = $settings->smtp_from_name; diff --git a/app/Livewire/Notifications/Pushover.php b/app/Livewire/Notifications/Pushover.php index bd5ab79c8..9c7ff64ad 100644 --- a/app/Livewire/Notifications/Pushover.php +++ b/app/Livewire/Notifications/Pushover.php @@ -5,12 +5,15 @@ use App\Models\PushoverNotificationSettings; use App\Models\Team; use App\Notifications\Test; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; class Pushover extends Component { + use AuthorizesRequests; + protected $listeners = ['refresh' => '$refresh']; #[Locked] @@ -72,6 +75,7 @@ public function mount() try { $this->team = auth()->user()->currentTeam(); $this->settings = $this->team->pushoverNotificationSettings; + $this->authorize('view', $this->settings); $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); @@ -82,6 +86,7 @@ public function syncData(bool $toModel = false) { if ($toModel) { $this->validate(); + $this->authorize('update', $this->settings); $this->settings->pushover_enabled = $this->pushoverEnabled; $this->settings->pushover_user_key = $this->pushoverUserKey; $this->settings->pushover_api_token = $this->pushoverApiToken; @@ -175,6 +180,7 @@ public function saveModel() public function sendTestNotification() { try { + $this->authorize('sendTest', $this->settings); $this->team->notify(new Test(channel: 'pushover')); $this->dispatch('success', 'Test notification sent.'); } catch (\Throwable $e) { diff --git a/app/Livewire/Notifications/Slack.php b/app/Livewire/Notifications/Slack.php index 9c847ce57..d21399c42 100644 --- a/app/Livewire/Notifications/Slack.php +++ b/app/Livewire/Notifications/Slack.php @@ -5,12 +5,15 @@ use App\Models\SlackNotificationSettings; use App\Models\Team; use App\Notifications\Test; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; class Slack extends Component { + use AuthorizesRequests; + protected $listeners = ['refresh' => '$refresh']; #[Locked] @@ -69,6 +72,7 @@ public function mount() try { $this->team = auth()->user()->currentTeam(); $this->settings = $this->team->slackNotificationSettings; + $this->authorize('view', $this->settings); $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); @@ -79,6 +83,7 @@ public function syncData(bool $toModel = false) { if ($toModel) { $this->validate(); + $this->authorize('update', $this->settings); $this->settings->slack_enabled = $this->slackEnabled; $this->settings->slack_webhook_url = $this->slackWebhookUrl; @@ -168,6 +173,7 @@ public function saveModel() public function sendTestNotification() { try { + $this->authorize('sendTest', $this->settings); $this->team->notify(new Test(channel: 'slack')); $this->dispatch('success', 'Test notification sent.'); } catch (\Throwable $e) { diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php index 07393d4ea..ca9df47c1 100644 --- a/app/Livewire/Notifications/Telegram.php +++ b/app/Livewire/Notifications/Telegram.php @@ -5,12 +5,15 @@ use App\Models\Team; use App\Models\TelegramNotificationSettings; use App\Notifications\Test; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; class Telegram extends Component { + use AuthorizesRequests; + protected $listeners = ['refresh' => '$refresh']; #[Locked] @@ -111,6 +114,7 @@ public function mount() try { $this->team = auth()->user()->currentTeam(); $this->settings = $this->team->telegramNotificationSettings; + $this->authorize('view', $this->settings); $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); @@ -121,6 +125,7 @@ public function syncData(bool $toModel = false) { if ($toModel) { $this->validate(); + $this->authorize('update', $this->settings); $this->settings->telegram_enabled = $this->telegramEnabled; $this->settings->telegram_token = $this->telegramToken; $this->settings->telegram_chat_id = $this->telegramChatId; @@ -241,6 +246,7 @@ public function saveModel() public function sendTestNotification() { try { + $this->authorize('sendTest', $this->settings); $this->team->notify(new Test(channel: 'telegram')); $this->dispatch('success', 'Test notification sent.'); } catch (\Throwable $e) { diff --git a/app/Livewire/Project/Application/Preview/Form.php b/app/Livewire/Project/Application/Preview/Form.php index edcab44c8..ff951ec54 100644 --- a/app/Livewire/Project/Application/Preview/Form.php +++ b/app/Livewire/Project/Application/Preview/Form.php @@ -3,12 +3,15 @@ namespace App\Livewire\Project\Application\Preview; use App\Models\Application; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Attributes\Validate; use Livewire\Component; use Spatie\Url\Url; class Form extends Component { + use AuthorizesRequests; + public Application $application; #[Validate('required')] @@ -27,6 +30,7 @@ public function mount() public function submit() { try { + $this->authorize('update', $this->application); $this->resetErrorBag(); $this->validate(); $this->application->preview_url_template = str_replace(' ', '', $this->previewUrlTemplate); @@ -41,6 +45,7 @@ public function submit() public function resetToDefault() { try { + $this->authorize('update', $this->application); $this->application->preview_url_template = '{{pr_id}}.{{domain}}'; $this->previewUrlTemplate = $this->application->preview_url_template; $this->application->save(); diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php index 5f07b02f3..9164c1475 100644 --- a/app/Livewire/Project/Application/Previews.php +++ b/app/Livewire/Project/Application/Previews.php @@ -38,6 +38,7 @@ public function mount() public function load_prs() { try { + $this->authorize('update', $this->application); ['rate_limit_remaining' => $rate_limit_remaining, 'data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/pulls"); $this->rate_limit_remaining = $rate_limit_remaining; $this->pull_requests = $data->sortBy('number')->values(); diff --git a/app/Livewire/Project/Database/Import.php b/app/Livewire/Project/Database/Import.php index 6622abaaf..3f974f63d 100644 --- a/app/Livewire/Project/Database/Import.php +++ b/app/Livewire/Project/Database/Import.php @@ -176,6 +176,7 @@ public function runImport() return; } try { + $this->importRunning = true; $this->importCommands = []; if (filled($this->customLocation)) { $backupFileName = '/tmp/restore_'.$this->resource->uuid; diff --git a/app/Livewire/Project/Index.php b/app/Livewire/Project/Index.php index 5347d74f0..5381fa78d 100644 --- a/app/Livewire/Project/Index.php +++ b/app/Livewire/Project/Index.php @@ -20,6 +20,7 @@ public function mount() $this->private_keys = PrivateKey::ownedByCurrentTeam()->get(); $this->projects = Project::ownedByCurrentTeam()->get()->map(function ($project) { $project->settingsRoute = route('project.edit', ['project_uuid' => $project->uuid]); + $project->canUpdate = auth()->user()->can('update', $project); return $project; }); diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php index 8ac74e7de..559851e3a 100644 --- a/app/Livewire/Project/Service/Configuration.php +++ b/app/Livewire/Project/Service/Configuration.php @@ -3,11 +3,14 @@ namespace App\Livewire\Project\Service; use App\Models\Service; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Auth; use Livewire\Component; class Configuration extends Component { + use AuthorizesRequests; + public $currentRoute; public $project; @@ -40,24 +43,30 @@ public function render() public function mount() { - $this->parameters = get_route_parameters(); - $this->currentRoute = request()->route()->getName(); - $this->query = request()->query(); - $project = currentTeam() - ->projects() - ->select('id', 'uuid', 'team_id') - ->where('uuid', request()->route('project_uuid')) - ->firstOrFail(); - $environment = $project->environments() - ->select('id', 'uuid', 'name', 'project_id') - ->where('uuid', request()->route('environment_uuid')) - ->firstOrFail(); - $this->service = $environment->services()->whereUuid(request()->route('service_uuid'))->firstOrFail(); + try { + $this->parameters = get_route_parameters(); + $this->currentRoute = request()->route()->getName(); + $this->query = request()->query(); + $project = currentTeam() + ->projects() + ->select('id', 'uuid', 'team_id') + ->where('uuid', request()->route('project_uuid')) + ->firstOrFail(); + $environment = $project->environments() + ->select('id', 'uuid', 'name', 'project_id') + ->where('uuid', request()->route('environment_uuid')) + ->firstOrFail(); + $this->service = $environment->services()->whereUuid(request()->route('service_uuid'))->firstOrFail(); - $this->project = $project; - $this->environment = $environment; - $this->applications = $this->service->applications->sort(); - $this->databases = $this->service->databases->sort(); + $this->authorize('view', $this->service); + + $this->project = $project; + $this->environment = $environment; + $this->applications = $this->service->applications->sort(); + $this->databases = $this->service->databases->sort(); + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function refreshServices() @@ -70,6 +79,7 @@ public function refreshServices() public function restartApplication($id) { try { + $this->authorize('update', $this->service); $application = $this->service->applications->find($id); if ($application) { $application->restart(); @@ -83,6 +93,7 @@ public function restartApplication($id) public function restartDatabase($id) { try { + $this->authorize('update', $this->service); $database = $this->service->databases->find($id); if ($database) { $database->restart(); diff --git a/app/Livewire/Project/Service/Database.php b/app/Livewire/Project/Service/Database.php index 0af757c8c..abf4c45a7 100644 --- a/app/Livewire/Project/Service/Database.php +++ b/app/Livewire/Project/Service/Database.php @@ -6,6 +6,7 @@ use App\Actions\Database\StopDatabaseProxy; use App\Models\InstanceSettings; use App\Models\ServiceDatabase; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; @@ -13,6 +14,8 @@ class Database extends Component { + use AuthorizesRequests; + public ServiceDatabase $database; public ?string $db_url_public = null; @@ -40,24 +43,31 @@ public function render() public function mount() { - $this->parameters = get_route_parameters(); - if ($this->database->is_public) { - $this->db_url_public = $this->database->getServiceDatabaseUrl(); + try { + $this->parameters = get_route_parameters(); + $this->authorize('view', $this->database); + if ($this->database->is_public) { + $this->db_url_public = $this->database->getServiceDatabaseUrl(); + } + $this->refreshFileStorages(); + } catch (\Throwable $e) { + return handleError($e, $this); } - $this->refreshFileStorages(); } public function delete($password) { - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } - } - try { + $this->authorize('delete', $this->database); + + if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { + if (! Hash::check($password, Auth::user()->password)) { + $this->addError('password', 'The provided password is incorrect.'); + + return; + } + } + $this->database->delete(); $this->dispatch('success', 'Database deleted.'); @@ -69,24 +79,35 @@ public function delete($password) public function instantSaveExclude() { - $this->submit(); + try { + $this->authorize('update', $this->database); + $this->submit(); + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function instantSaveLogDrain() { - if (! $this->database->service->destination->server->isLogDrainEnabled()) { - $this->database->is_log_drain_enabled = false; - $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); + try { + $this->authorize('update', $this->database); + if (! $this->database->service->destination->server->isLogDrainEnabled()) { + $this->database->is_log_drain_enabled = false; + $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); - return; + return; + } + $this->submit(); + $this->dispatch('success', 'You need to restart the service for the changes to take effect.'); + } catch (\Throwable $e) { + return handleError($e, $this); } - $this->submit(); - $this->dispatch('success', 'You need to restart the service for the changes to take effect.'); } public function convertToApplication() { try { + $this->authorize('update', $this->database); $service = $this->database->service; $serviceDatabase = $this->database; @@ -122,28 +143,33 @@ public function convertToApplication() public function instantSave() { - if ($this->database->is_public && ! $this->database->public_port) { - $this->dispatch('error', 'Public port is required.'); - $this->database->is_public = false; - - return; - } - if ($this->database->is_public) { - if (! str($this->database->status)->startsWith('running')) { - $this->dispatch('error', 'Database must be started to be publicly accessible.'); + try { + $this->authorize('update', $this->database); + if ($this->database->is_public && ! $this->database->public_port) { + $this->dispatch('error', 'Public port is required.'); $this->database->is_public = false; return; } - StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->getServiceDatabaseUrl(); - $this->dispatch('success', 'Database is now publicly accessible.'); - } else { - StopDatabaseProxy::run($this->database); - $this->db_url_public = null; - $this->dispatch('success', 'Database is no longer publicly accessible.'); + if ($this->database->is_public) { + if (! str($this->database->status)->startsWith('running')) { + $this->dispatch('error', 'Database must be started to be publicly accessible.'); + $this->database->is_public = false; + + return; + } + StartDatabaseProxy::run($this->database); + $this->db_url_public = $this->database->getServiceDatabaseUrl(); + $this->dispatch('success', 'Database is now publicly accessible.'); + } else { + StopDatabaseProxy::run($this->database); + $this->db_url_public = null; + $this->dispatch('success', 'Database is no longer publicly accessible.'); + } + $this->submit(); + } catch (\Throwable $e) { + return handleError($e, $this); } - $this->submit(); } public function refreshFileStorages() @@ -154,11 +180,13 @@ public function refreshFileStorages() public function submit() { try { + $this->authorize('update', $this->database); $this->validate(); $this->database->save(); updateCompose($this->database); $this->dispatch('success', 'Database saved.'); - } catch (\Throwable) { + } catch (\Throwable $e) { + return handleError($e, $this); } finally { $this->dispatch('generateDockerCompose'); } diff --git a/app/Livewire/Project/Service/FileStorage.php b/app/Livewire/Project/Service/FileStorage.php index 5b88c15eb..2933a8cca 100644 --- a/app/Livewire/Project/Service/FileStorage.php +++ b/app/Livewire/Project/Service/FileStorage.php @@ -15,12 +15,15 @@ use App\Models\StandaloneMysql; use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Livewire\Component; class FileStorage extends Component { + use AuthorizesRequests; + public LocalFileVolume $fileStorage; public ServiceApplication|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase|Application $resource; @@ -54,6 +57,8 @@ public function mount() public function convertToDirectory() { try { + $this->authorize('update', $this->resource); + $this->fileStorage->deleteStorageOnServer(); $this->fileStorage->is_directory = true; $this->fileStorage->content = null; @@ -70,6 +75,8 @@ public function convertToDirectory() public function loadStorageOnServer() { try { + $this->authorize('update', $this->resource); + $this->fileStorage->loadStorageOnServer(); $this->dispatch('success', 'File storage loaded from server.'); } catch (\Throwable $e) { @@ -82,6 +89,8 @@ public function loadStorageOnServer() public function convertToFile() { try { + $this->authorize('update', $this->resource); + $this->fileStorage->deleteStorageOnServer(); $this->fileStorage->is_directory = false; $this->fileStorage->content = null; @@ -99,6 +108,8 @@ public function convertToFile() public function delete($password) { + $this->authorize('update', $this->resource); + if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { if (! Hash::check($password, Auth::user()->password)) { $this->addError('password', 'The provided password is incorrect.'); @@ -127,6 +138,8 @@ public function delete($password) public function submit() { + $this->authorize('update', $this->resource); + $original = $this->fileStorage->getOriginal(); try { $this->validate(); diff --git a/app/Livewire/Project/Service/Index.php b/app/Livewire/Project/Service/Index.php index 39f4e106d..8d37d3e31 100644 --- a/app/Livewire/Project/Service/Index.php +++ b/app/Livewire/Project/Service/Index.php @@ -5,11 +5,14 @@ use App\Models\Service; use App\Models\ServiceApplication; use App\Models\ServiceDatabase; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Collection; use Livewire\Component; class Index extends Component { + use AuthorizesRequests; + public ?Service $service = null; public ?ServiceApplication $serviceApplication = null; @@ -36,6 +39,7 @@ public function mount() if (! $this->service) { return redirect()->route('dashboard'); } + $this->authorize('view', $this->service); $service = $this->service->applications()->whereUuid($this->parameters['stack_service_uuid'])->first(); if ($service) { $this->serviceApplication = $service; @@ -52,7 +56,12 @@ public function mount() public function generateDockerCompose() { - $this->service->parse(); + try { + $this->authorize('update', $this->service); + $this->service->parse(); + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function render() diff --git a/app/Livewire/Project/Service/ServiceApplicationView.php b/app/Livewire/Project/Service/ServiceApplicationView.php index 64f7ab95c..5e178374b 100644 --- a/app/Livewire/Project/Service/ServiceApplicationView.php +++ b/app/Livewire/Project/Service/ServiceApplicationView.php @@ -4,6 +4,7 @@ use App\Models\InstanceSettings; use App\Models\ServiceApplication; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; @@ -12,6 +13,8 @@ class ServiceApplicationView extends Component { + use AuthorizesRequests; + public ServiceApplication $application; public $parameters; @@ -34,32 +37,44 @@ class ServiceApplicationView extends Component public function instantSave() { - $this->submit(); + try { + $this->authorize('update', $this->application); + $this->submit(); + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function instantSaveAdvanced() { - if (! $this->application->service->destination->server->isLogDrainEnabled()) { - $this->application->is_log_drain_enabled = false; - $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); + try { + $this->authorize('update', $this->application); + if (! $this->application->service->destination->server->isLogDrainEnabled()) { + $this->application->is_log_drain_enabled = false; + $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); - return; + return; + } + $this->application->save(); + $this->dispatch('success', 'You need to restart the service for the changes to take effect.'); + } catch (\Throwable $e) { + return handleError($e, $this); } - $this->application->save(); - $this->dispatch('success', 'You need to restart the service for the changes to take effect.'); } public function delete($password) { - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } - } - try { + $this->authorize('delete', $this->application); + + if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { + if (! Hash::check($password, Auth::user()->password)) { + $this->addError('password', 'The provided password is incorrect.'); + + return; + } + } + $this->application->delete(); $this->dispatch('success', 'Application deleted.'); @@ -71,12 +86,18 @@ public function delete($password) public function mount() { - $this->parameters = get_route_parameters(); + try { + $this->parameters = get_route_parameters(); + $this->authorize('view', $this->application); + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function convertToDatabase() { try { + $this->authorize('update', $this->application); $service = $this->application->service; $serviceApplication = $this->application; @@ -111,6 +132,7 @@ public function convertToDatabase() public function submit() { try { + $this->authorize('update', $this->application); $this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim(); $this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim(); $this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) { diff --git a/app/Livewire/Project/Service/Storage.php b/app/Livewire/Project/Service/Storage.php index 4b64a8b5e..26cd54425 100644 --- a/app/Livewire/Project/Service/Storage.php +++ b/app/Livewire/Project/Service/Storage.php @@ -3,10 +3,13 @@ namespace App\Livewire\Project\Service; use App\Models\LocalPersistentVolume; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Storage extends Component { + use AuthorizesRequests; + public $resource; public $fileStorage; @@ -42,6 +45,8 @@ public function refreshStorages() public function addNewVolume($data) { try { + $this->authorize('update', $this->resource); + LocalPersistentVolume::create([ 'name' => $data['name'], 'mount_path' => $data['mount_path'], diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php index 0dbf0f957..cf7843f84 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php @@ -2,10 +2,13 @@ namespace App\Livewire\Project\Shared\EnvironmentVariable; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Add extends Component { + use AuthorizesRequests; + public $parameters; public bool $shared = false; diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php index 966d626b1..1a9daf77b 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php @@ -5,11 +5,12 @@ use App\Models\EnvironmentVariable as ModelsEnvironmentVariable; use App\Models\SharedEnvironmentVariable; use App\Traits\EnvironmentVariableProtection; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Show extends Component { - use EnvironmentVariableProtection; + use AuthorizesRequests, EnvironmentVariableProtection; public $parameters; @@ -75,6 +76,11 @@ public function mount() } } + public function getResourceProperty() + { + return $this->env->resourceable ?? $this->env; + } + public function refresh() { $this->syncData(); @@ -140,6 +146,8 @@ public function serialize() public function lock() { + $this->authorize('update', $this->env); + $this->env->is_shown_once = true; if ($this->isSharedVariable) { unset($this->env->is_required); @@ -158,6 +166,8 @@ public function instantSave() public function submit() { try { + $this->authorize('update', $this->env); + if (! $this->isSharedVariable && $this->is_required && str($this->value)->isEmpty()) { $oldValue = $this->env->getOriginal('value'); $this->value = $oldValue; @@ -179,9 +189,11 @@ public function submit() public function delete() { try { + $this->authorize('delete', $this->env); + // Check if the variable is used in Docker Compose - if ($this->type === 'service' || $this->type === 'application' && $this->env->resource()?->docker_compose) { - [$isUsed, $reason] = $this->isEnvironmentVariableUsedInDockerCompose($this->env->key, $this->env->resource()?->docker_compose); + if ($this->type === 'service' || $this->type === 'application' && $this->env->resourceable?->docker_compose) { + [$isUsed, $reason] = $this->isEnvironmentVariableUsedInDockerCompose($this->env->key, $this->env->resourceable?->docker_compose); if ($isUsed) { $this->dispatch('error', "Cannot delete environment variable '{$this->env->key}'

Please remove it from the Docker Compose file first."); diff --git a/app/Livewire/Project/Shared/HealthChecks.php b/app/Livewire/Project/Shared/HealthChecks.php index 83162e36a..ae94f7cf2 100644 --- a/app/Livewire/Project/Shared/HealthChecks.php +++ b/app/Livewire/Project/Shared/HealthChecks.php @@ -2,10 +2,13 @@ namespace App\Livewire\Project\Shared; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class HealthChecks extends Component { + use AuthorizesRequests; + public $resource; protected $rules = [ @@ -27,6 +30,7 @@ class HealthChecks extends Component public function instantSave() { + $this->authorize('update', $this->resource); $this->resource->save(); $this->dispatch('success', 'Health check updated.'); } @@ -34,6 +38,7 @@ public function instantSave() public function submit() { try { + $this->authorize('update', $this->resource); $this->validate(); $this->resource->save(); $this->dispatch('success', 'Health check updated.'); diff --git a/app/Livewire/Project/Shared/ResourceLimits.php b/app/Livewire/Project/Shared/ResourceLimits.php index 608dfbf02..196badec8 100644 --- a/app/Livewire/Project/Shared/ResourceLimits.php +++ b/app/Livewire/Project/Shared/ResourceLimits.php @@ -2,10 +2,13 @@ namespace App\Livewire\Project\Shared; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class ResourceLimits extends Component { + use AuthorizesRequests; + public $resource; protected $rules = [ @@ -31,6 +34,7 @@ class ResourceLimits extends Component public function submit() { try { + $this->authorize('update', $this->resource); if (! $this->resource->limits_memory) { $this->resource->limits_memory = '0'; } diff --git a/app/Livewire/Project/Shared/ScheduledTask/Add.php b/app/Livewire/Project/Shared/ScheduledTask/Add.php index c286fee5a..e4b666532 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Add.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Add.php @@ -3,12 +3,15 @@ namespace App\Livewire\Project\Shared\ScheduledTask; use App\Models\ScheduledTask; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Collection; use Livewire\Attributes\Locked; use Livewire\Component; class Add extends Component { + use AuthorizesRequests; + public $parameters; #[Locked] @@ -20,6 +23,9 @@ class Add extends Component #[Locked] public Collection $containerNames; + #[Locked] + public $resource; + public string $name; public string $command; @@ -45,6 +51,22 @@ class Add extends Component public function mount() { $this->parameters = get_route_parameters(); + + // Get the resource based on type and id + switch ($this->type) { + case 'application': + $this->resource = \App\Models\Application::findOrFail($this->id); + break; + case 'service': + $this->resource = \App\Models\Service::findOrFail($this->id); + break; + case 'standalone-postgresql': + $this->resource = \App\Models\StandalonePostgresql::findOrFail($this->id); + break; + default: + throw new \Exception('Invalid resource type'); + } + if ($this->containerNames->count() > 0) { $this->container = $this->containerNames->first(); } @@ -53,6 +75,7 @@ public function mount() public function submit() { try { + $this->authorize('update', $this->resource); $this->validate(); $isValid = validate_cron_expression($this->frequency); if (! $isValid) { diff --git a/app/Livewire/Project/Shared/ScheduledTask/Show.php b/app/Livewire/Project/Shared/ScheduledTask/Show.php index fe6e36d5c..c8d07ae36 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Show.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Show.php @@ -6,12 +6,15 @@ use App\Models\Application; use App\Models\ScheduledTask; use App\Models\Service; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; class Show extends Component { + use AuthorizesRequests; + public Application|Service $resource; public ScheduledTask $task; @@ -109,6 +112,7 @@ public function syncData(bool $toModel = false) public function instantSave() { try { + $this->authorize('update', $this->resource); $this->syncData(true); $this->dispatch('success', 'Scheduled task updated.'); $this->refreshTasks(); @@ -120,6 +124,7 @@ public function instantSave() public function submit() { try { + $this->authorize('update', $this->resource); $this->syncData(true); $this->dispatch('success', 'Scheduled task updated.'); } catch (\Exception $e) { @@ -139,6 +144,7 @@ public function refreshTasks() public function delete() { try { + $this->authorize('update', $this->resource); $this->task->delete(); if ($this->type === 'application') { @@ -154,6 +160,7 @@ public function delete() public function executeNow() { try { + $this->authorize('update', $this->resource); ScheduledTaskJob::dispatch($this->task); $this->dispatch('success', 'Scheduled task executed.'); } catch (\Exception $e) { diff --git a/app/Livewire/Project/Shared/Storages/Add.php b/app/Livewire/Project/Shared/Storages/Add.php index dc015386c..006d41c14 100644 --- a/app/Livewire/Project/Shared/Storages/Add.php +++ b/app/Livewire/Project/Shared/Storages/Add.php @@ -4,10 +4,13 @@ use App\Models\Application; use App\Models\LocalFileVolume; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Add extends Component { + use AuthorizesRequests; + public $resource; public $uuid; @@ -77,6 +80,8 @@ public function mount() public function submitFileStorage() { try { + $this->authorize('update', $this->resource); + $this->validate([ 'file_storage_path' => 'string', 'file_storage_content' => 'nullable|string', @@ -112,6 +117,8 @@ public function submitFileStorage() public function submitFileStorageDirectory() { try { + $this->authorize('update', $this->resource); + $this->validate([ 'file_storage_directory_source' => 'string', 'file_storage_directory_destination' => 'string', @@ -140,6 +147,8 @@ public function submitFileStorageDirectory() public function submitPersistentVolume() { try { + $this->authorize('update', $this->resource); + $this->validate([ 'name' => 'required|string', 'mount_path' => 'required|string', diff --git a/app/Livewire/Project/Shared/Storages/Show.php b/app/Livewire/Project/Shared/Storages/Show.php index 54b1be3af..3928ee1d4 100644 --- a/app/Livewire/Project/Shared/Storages/Show.php +++ b/app/Livewire/Project/Shared/Storages/Show.php @@ -4,14 +4,19 @@ use App\Models\InstanceSettings; use App\Models\LocalPersistentVolume; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Livewire\Component; class Show extends Component { + use AuthorizesRequests; + public LocalPersistentVolume $storage; + public $resource; + public bool $isReadOnly = false; public bool $isFirst = true; @@ -34,6 +39,8 @@ class Show extends Component public function submit() { + $this->authorize('update', $this->resource); + $this->validate(); $this->storage->save(); $this->dispatch('success', 'Storage updated successfully'); @@ -41,6 +48,8 @@ public function submit() public function delete($password) { + $this->authorize('update', $this->resource); + if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { if (! Hash::check($password, Auth::user()->password)) { $this->addError('password', 'The provided password is incorrect.'); diff --git a/app/Livewire/Project/Shared/Tags.php b/app/Livewire/Project/Shared/Tags.php index 811859cb8..37b8b277a 100644 --- a/app/Livewire/Project/Shared/Tags.php +++ b/app/Livewire/Project/Shared/Tags.php @@ -3,12 +3,15 @@ namespace App\Livewire\Project\Shared; use App\Models\Tag; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Attributes\Validate; use Livewire\Component; // Refactored ✅ class Tags extends Component { + use AuthorizesRequests; + public $resource = null; #[Validate('required|string|min:2')] @@ -34,6 +37,7 @@ public function loadTags() public function submit() { try { + $this->authorize('update', $this->resource); $this->validate(); $tags = str($this->newTags)->trim()->explode(' '); foreach ($tags as $tag) { @@ -66,6 +70,7 @@ public function submit() public function addTag(string $id, string $name) { try { + $this->authorize('update', $this->resource); $name = strip_tags($name); if ($this->resource->tags()->where('id', $id)->exists()) { $this->dispatch('error', 'Duplicate tags.', "Tag $name already added."); @@ -83,6 +88,7 @@ public function addTag(string $id, string $name) public function deleteTag(string $id) { try { + $this->authorize('update', $this->resource); $this->resource->tags()->detach($id); $found_more_tags = Tag::ownedByCurrentTeam()->find($id); if ($found_more_tags && $found_more_tags->applications()->count() == 0 && $found_more_tags->services()->count() == 0) { diff --git a/app/Livewire/Project/Shared/Webhooks.php b/app/Livewire/Project/Shared/Webhooks.php index 57c65c4dd..eafc653d5 100644 --- a/app/Livewire/Project/Shared/Webhooks.php +++ b/app/Livewire/Project/Shared/Webhooks.php @@ -2,11 +2,14 @@ namespace App\Livewire\Project\Shared; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; // Refactored ✅ class Webhooks extends Component { + use AuthorizesRequests; + public $resource; public ?string $deploywebhook; diff --git a/app/Livewire/Security/ApiTokens.php b/app/Livewire/Security/ApiTokens.php index 72684bdc6..a263acedf 100644 --- a/app/Livewire/Security/ApiTokens.php +++ b/app/Livewire/Security/ApiTokens.php @@ -3,10 +3,14 @@ namespace App\Livewire\Security; use App\Models\InstanceSettings; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Laravel\Sanctum\PersonalAccessToken; use Livewire\Component; class ApiTokens extends Component { + use AuthorizesRequests; + public ?string $description = null; public $tokens = []; @@ -15,6 +19,10 @@ class ApiTokens extends Component public $isApiEnabled; + public bool $canUseRootPermissions = false; + + public bool $canUseWritePermissions = false; + public function render() { return view('livewire.security.api-tokens'); @@ -23,6 +31,8 @@ public function render() public function mount() { $this->isApiEnabled = InstanceSettings::get()->is_api_enabled; + $this->canUseRootPermissions = auth()->user()->can('useRootPermissions', PersonalAccessToken::class); + $this->canUseWritePermissions = auth()->user()->can('useWritePermissions', PersonalAccessToken::class); $this->getTokens(); } @@ -33,6 +43,23 @@ private function getTokens() public function updatedPermissions($permissionToUpdate) { + // Check if user is trying to use restricted permissions + if ($permissionToUpdate == 'root' && ! $this->canUseRootPermissions) { + $this->dispatch('error', 'You do not have permission to use root permissions.'); + // Remove root from permissions if it was somehow added + $this->permissions = array_diff($this->permissions, ['root']); + + return; + } + + if (in_array($permissionToUpdate, ['write', 'write:sensitive']) && ! $this->canUseWritePermissions) { + $this->dispatch('error', 'You do not have permission to use write permissions.'); + // Remove write permissions if they were somehow added + $this->permissions = array_diff($this->permissions, ['write', 'write:sensitive']); + + return; + } + if ($permissionToUpdate == 'root') { $this->permissions = ['root']; } elseif ($permissionToUpdate == 'read:sensitive' && ! in_array('read', $this->permissions)) { @@ -50,6 +77,17 @@ public function updatedPermissions($permissionToUpdate) public function addNewToken() { try { + $this->authorize('create', PersonalAccessToken::class); + + // Validate permissions based on user role + if (in_array('root', $this->permissions) && ! $this->canUseRootPermissions) { + throw new \Exception('You do not have permission to create tokens with root permissions.'); + } + + if (array_intersect(['write', 'write:sensitive'], $this->permissions) && ! $this->canUseWritePermissions) { + throw new \Exception('You do not have permission to create tokens with write permissions.'); + } + $this->validate([ 'description' => 'required|min:3|max:255', ]); @@ -65,6 +103,7 @@ public function revoke(int $id) { try { $token = auth()->user()->tokens()->where('id', $id)->firstOrFail(); + $this->authorize('delete', $token); $token->delete(); $this->getTokens(); } catch (\Exception $e) { diff --git a/app/Livewire/Security/PrivateKey/Index.php b/app/Livewire/Security/PrivateKey/Index.php index 76441a67e..950ec152d 100644 --- a/app/Livewire/Security/PrivateKey/Index.php +++ b/app/Livewire/Security/PrivateKey/Index.php @@ -3,10 +3,13 @@ namespace App\Livewire\Security\PrivateKey; use App\Models\PrivateKey; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Index extends Component { + use AuthorizesRequests; + public function render() { $privateKeys = PrivateKey::ownedByCurrentTeam(['name', 'uuid', 'is_git_related', 'description'])->get(); @@ -18,6 +21,7 @@ public function render() public function cleanupUnusedKeys() { + $this->authorize('create', PrivateKey::class); PrivateKey::cleanupUnusedKeys(); $this->dispatch('success', 'Unused keys have been cleaned up.'); } diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php index 392ad38fa..f377bbeb9 100644 --- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php +++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php @@ -3,12 +3,17 @@ namespace App\Livewire\Server\Proxy; use App\Models\Server; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class DynamicConfigurationNavbar extends Component { + use AuthorizesRequests; + public $server_id; + public Server $server; + public $fileName = ''; public $value = ''; @@ -17,18 +22,18 @@ class DynamicConfigurationNavbar extends Component public function delete(string $fileName) { - $server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first(); - $proxy_path = $server->proxyPath(); - $proxy_type = $server->proxyType(); + $this->authorize('update', $this->server); + $proxy_path = $this->server->proxyPath(); + $proxy_type = $this->server->proxyType(); $file = str_replace('|', '.', $fileName); if ($proxy_type === 'CADDY' && $file === 'Caddyfile') { $this->dispatch('error', 'Cannot delete Caddyfile.'); return; } - instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server); + instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $this->server); if ($proxy_type === 'CADDY') { - $server->reloadCaddy(); + $this->server->reloadCaddy(); } $this->dispatch('success', 'File deleted.'); $this->dispatch('loadDynamicConfigurations'); diff --git a/app/Livewire/SharedVariables/Environment/Show.php b/app/Livewire/SharedVariables/Environment/Show.php index e88ac5f13..bee757a64 100644 --- a/app/Livewire/SharedVariables/Environment/Show.php +++ b/app/Livewire/SharedVariables/Environment/Show.php @@ -4,10 +4,13 @@ use App\Models\Application; use App\Models\Project; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Show extends Component { + use AuthorizesRequests; + public Project $project; public Application $application; @@ -21,6 +24,8 @@ class Show extends Component public function saveKey($data) { try { + $this->authorize('update', $this->environment); + $found = $this->environment->environment_variables()->where('key', $data['key'])->first(); if ($found) { throw new \Exception('Variable already exists.'); diff --git a/app/Livewire/SharedVariables/Project/Show.php b/app/Livewire/SharedVariables/Project/Show.php index 0171283c4..712a9960b 100644 --- a/app/Livewire/SharedVariables/Project/Show.php +++ b/app/Livewire/SharedVariables/Project/Show.php @@ -3,10 +3,13 @@ namespace App\Livewire\SharedVariables\Project; use App\Models\Project; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Show extends Component { + use AuthorizesRequests; + public Project $project; protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey', 'environmentVariableDeleted' => '$refresh']; @@ -14,6 +17,8 @@ class Show extends Component public function saveKey($data) { try { + $this->authorize('update', $this->project); + $found = $this->project->environment_variables()->where('key', $data['key'])->first(); if ($found) { throw new \Exception('Variable already exists.'); diff --git a/app/Livewire/SharedVariables/Team/Index.php b/app/Livewire/SharedVariables/Team/Index.php index a76ccf58a..82473528c 100644 --- a/app/Livewire/SharedVariables/Team/Index.php +++ b/app/Livewire/SharedVariables/Team/Index.php @@ -3,10 +3,13 @@ namespace App\Livewire\SharedVariables\Team; use App\Models\Team; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Index extends Component { + use AuthorizesRequests; + public Team $team; protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey', 'environmentVariableDeleted' => '$refresh']; @@ -14,6 +17,8 @@ class Index extends Component public function saveKey($data) { try { + $this->authorize('update', $this->team); + $found = $this->team->environment_variables()->where('key', $data['key'])->first(); if ($found) { throw new \Exception('Variable already exists.'); diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index e73c9dc73..9ad5444b9 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -5,6 +5,7 @@ use App\Jobs\GithubAppPermissionJob; use App\Models\GithubApp; use App\Models\PrivateKey; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Http; use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Key\InMemory; @@ -13,7 +14,9 @@ class Change extends Component { - public string $webhook_endpoint; + use AuthorizesRequests; + + public string $webhook_endpoint = ''; public ?string $ipv4 = null; @@ -69,6 +72,8 @@ public function boot() public function checkPermissions() { try { + $this->authorize('view', $this->github_app); + GithubAppPermissionJob::dispatchSync($this->github_app); $this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->dispatch('success', 'Github App permissions updated.'); @@ -155,7 +160,7 @@ public function mount() if (isCloud() && ! isDev()) { $this->webhook_endpoint = config('app.url'); } else { - $this->webhook_endpoint = $this->ipv4; + $this->webhook_endpoint = $this->ipv4 ?? ''; $this->is_system_wide = $this->github_app->is_system_wide; } } catch (\Throwable $e) { @@ -195,6 +200,8 @@ private function generateGithubJwt($private_key, $app_id): string public function updateGithubAppName() { try { + $this->authorize('update', $this->github_app); + $privateKey = PrivateKey::ownedByCurrentTeam()->find($this->github_app->private_key_id); if (! $privateKey) { @@ -237,6 +244,8 @@ public function updateGithubAppName() public function submit() { try { + $this->authorize('update', $this->github_app); + $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->validate([ 'github_app.name' => 'required|string', @@ -262,6 +271,8 @@ public function submit() public function createGithubAppManually() { + $this->authorize('update', $this->github_app); + $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->github_app->app_id = '1234567890'; $this->github_app->installation_id = '1234567890'; @@ -272,6 +283,8 @@ public function createGithubAppManually() public function instantSave() { try { + $this->authorize('update', $this->github_app); + $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->github_app->save(); $this->dispatch('success', 'Github App updated.'); @@ -283,6 +296,8 @@ public function instantSave() public function delete() { try { + $this->authorize('delete', $this->github_app); + if ($this->github_app->applications->isNotEmpty()) { $this->dispatch('error', 'This source is being used by an application. Please delete all applications first.'); $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); diff --git a/app/Livewire/Source/Github/Create.php b/app/Livewire/Source/Github/Create.php index 136d3525e..f5d851b64 100644 --- a/app/Livewire/Source/Github/Create.php +++ b/app/Livewire/Source/Github/Create.php @@ -3,10 +3,13 @@ namespace App\Livewire\Source\Github; use App\Models\GithubApp; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Create extends Component { + use AuthorizesRequests; + public string $name; public ?string $organization = null; @@ -29,6 +32,8 @@ public function mount() public function createGitHubApp() { try { + $this->authorize('createAnyResource'); + $this->validate([ 'name' => 'required|string', 'organization' => 'nullable|string', diff --git a/app/Livewire/Storage/Create.php b/app/Livewire/Storage/Create.php index 9cbc516da..9efeb948c 100644 --- a/app/Livewire/Storage/Create.php +++ b/app/Livewire/Storage/Create.php @@ -4,11 +4,14 @@ use App\Models\S3Storage; use App\Support\ValidationPatterns; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Uri; use Livewire\Component; class Create extends Component { + use AuthorizesRequests; + public string $name; public string $description; @@ -94,6 +97,8 @@ public function updatedEndpoint($value) public function submit() { try { + $this->authorize('create', S3Storage::class); + $this->validate(); $this->storage = new S3Storage; $this->storage->name = $this->name; diff --git a/app/Livewire/Storage/Form.php b/app/Livewire/Storage/Form.php index fd2222d32..41541f6b9 100644 --- a/app/Livewire/Storage/Form.php +++ b/app/Livewire/Storage/Form.php @@ -4,10 +4,13 @@ use App\Models\S3Storage; use App\Support\ValidationPatterns; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Form extends Component { + use AuthorizesRequests; + public S3Storage $storage; protected function rules(): array @@ -60,6 +63,8 @@ protected function messages(): array public function testConnection() { try { + $this->authorize('validateConnection', $this->storage); + $this->storage->testConnection(shouldSave: true); return $this->dispatch('success', 'Connection is working.', 'Tested with "ListObjectsV2" action.'); @@ -83,8 +88,10 @@ public function delete() public function submit() { - $this->validate(); try { + $this->authorize('update', $this->storage); + + $this->validate(); $this->testConnection(); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Team/Index.php b/app/Livewire/Team/Index.php index accaab8d9..8b9b70e14 100644 --- a/app/Livewire/Team/Index.php +++ b/app/Livewire/Team/Index.php @@ -5,12 +5,15 @@ use App\Models\Team; use App\Models\TeamInvitation; use App\Support\ValidationPatterns; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Livewire\Component; class Index extends Component { + use AuthorizesRequests; + public $invitations = []; public Team $team; @@ -58,6 +61,7 @@ public function submit() { $this->validate(); try { + $this->authorize('update', $this->team); $this->team->save(); refreshSession(); $this->dispatch('success', 'Team updated.'); @@ -69,6 +73,7 @@ public function submit() public function delete() { $currentTeam = currentTeam(); + $this->authorize('delete', $currentTeam); $currentTeam->delete(); $currentTeam->members->each(function ($user) use ($currentTeam) { diff --git a/app/Livewire/Team/Invitations.php b/app/Livewire/Team/Invitations.php index 3af0e0e92..523f640b9 100644 --- a/app/Livewire/Team/Invitations.php +++ b/app/Livewire/Team/Invitations.php @@ -4,10 +4,13 @@ use App\Models\TeamInvitation; use App\Models\User; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Invitations extends Component { + use AuthorizesRequests; + public $invitations; protected $listeners = ['refreshInvitations']; @@ -15,6 +18,8 @@ class Invitations extends Component public function deleteInvitation(int $invitation_id) { try { + $this->authorize('manageInvitations', currentTeam()); + $invitation = TeamInvitation::ownedByCurrentTeam()->findOrFail($invitation_id); $user = User::whereEmail($invitation->email)->first(); if (filled($user)) { diff --git a/app/Livewire/Team/InviteLink.php b/app/Livewire/Team/InviteLink.php index fb0c51e54..0bac39db8 100644 --- a/app/Livewire/Team/InviteLink.php +++ b/app/Livewire/Team/InviteLink.php @@ -4,6 +4,7 @@ use App\Models\TeamInvitation; use App\Models\User; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Hash; @@ -13,6 +14,8 @@ class InviteLink extends Component { + use AuthorizesRequests; + public string $email; public string $role = 'member'; @@ -40,6 +43,7 @@ public function viaLink() private function generateInviteLink(bool $sendEmail = false) { try { + $this->authorize('manageInvitations', currentTeam()); $this->validate(); if (auth()->user()->role() === 'admin' && $this->role === 'owner') { throw new \Exception('Admins cannot invite owners.'); diff --git a/app/Livewire/Team/Member.php b/app/Livewire/Team/Member.php index 890d640a0..96c98c637 100644 --- a/app/Livewire/Team/Member.php +++ b/app/Livewire/Team/Member.php @@ -4,16 +4,21 @@ use App\Enums\Role; use App\Models\User; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Cache; use Livewire\Component; class Member extends Component { + use AuthorizesRequests; + public User $member; public function makeAdmin() { try { + $this->authorize('manageMembers', currentTeam()); + if (Role::from(auth()->user()->role())->lt(Role::ADMIN) || Role::from($this->getMemberRole())->gt(auth()->user()->role())) { throw new \Exception('You are not authorized to perform this action.'); @@ -28,6 +33,8 @@ public function makeAdmin() public function makeOwner() { try { + $this->authorize('manageMembers', currentTeam()); + if (Role::from(auth()->user()->role())->lt(Role::OWNER) || Role::from($this->getMemberRole())->gt(auth()->user()->role())) { throw new \Exception('You are not authorized to perform this action.'); @@ -42,6 +49,8 @@ public function makeOwner() public function makeReadonly() { try { + $this->authorize('manageMembers', currentTeam()); + if (Role::from(auth()->user()->role())->lt(Role::ADMIN) || Role::from($this->getMemberRole())->gt(auth()->user()->role())) { throw new \Exception('You are not authorized to perform this action.'); @@ -56,6 +65,8 @@ public function makeReadonly() public function remove() { try { + $this->authorize('manageMembers', currentTeam()); + if (Role::from(auth()->user()->role())->lt(Role::ADMIN) || Role::from($this->getMemberRole())->gt(auth()->user()->role())) { throw new \Exception('You are not authorized to perform this action.'); diff --git a/app/Livewire/Team/Member/Index.php b/app/Livewire/Team/Member/Index.php index 00b745fe4..e057ba3f6 100644 --- a/app/Livewire/Team/Member/Index.php +++ b/app/Livewire/Team/Member/Index.php @@ -3,15 +3,19 @@ namespace App\Livewire\Team\Member; use App\Models\TeamInvitation; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class Index extends Component { + use AuthorizesRequests; + public $invitations = []; public function mount() { - if (auth()->user()->isAdminFromSession()) { + // Only load invitations for users who can manage them + if (auth()->user()->can('manageInvitations', currentTeam())) { $this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get(); } } diff --git a/app/Livewire/Terminal/Index.php b/app/Livewire/Terminal/Index.php index dfeb5da66..03dbc1d91 100644 --- a/app/Livewire/Terminal/Index.php +++ b/app/Livewire/Terminal/Index.php @@ -18,7 +18,6 @@ class Index extends Component public function mount() { - $this->authorize('useTerminal', Server::class); $this->servers = Server::isReachable()->get()->filter(function ($server) { return $server->isTerminalEnabled(); }); diff --git a/app/Models/SharedEnvironmentVariable.php b/app/Models/SharedEnvironmentVariable.php index aab8b8735..7956f006a 100644 --- a/app/Models/SharedEnvironmentVariable.php +++ b/app/Models/SharedEnvironmentVariable.php @@ -12,4 +12,19 @@ class SharedEnvironmentVariable extends Model 'key' => 'string', 'value' => 'encrypted', ]; + + public function team() + { + return $this->belongsTo(Team::class); + } + + public function project() + { + return $this->belongsTo(Project::class); + } + + public function environment() + { + return $this->belongsTo(Environment::class); + } } diff --git a/app/Policies/ApiTokenPolicy.php b/app/Policies/ApiTokenPolicy.php new file mode 100644 index 000000000..761227118 --- /dev/null +++ b/app/Policies/ApiTokenPolicy.php @@ -0,0 +1,109 @@ +id === $token->tokenable_id && $token->tokenable_type === User::class; + */ + return true; + } + + /** + * Determine whether the user can create API tokens. + */ + public function create(User $user): bool + { + // Authorization temporarily disabled + /* + // All authenticated users can create their own API tokens + return true; + */ + return true; + } + + /** + * Determine whether the user can update the API token. + */ + public function update(User $user, PersonalAccessToken $token): bool + { + // Authorization temporarily disabled + /* + // Users can only update their own tokens + return $user->id === $token->tokenable_id && $token->tokenable_type === User::class; + */ + return true; + } + + /** + * Determine whether the user can delete the API token. + */ + public function delete(User $user, PersonalAccessToken $token): bool + { + // Authorization temporarily disabled + /* + // Users can only delete their own tokens + return $user->id === $token->tokenable_id && $token->tokenable_type === User::class; + */ + return true; + } + + /** + * Determine whether the user can manage their own API tokens. + */ + public function manage(User $user): bool + { + // Authorization temporarily disabled + /* + // All authenticated users can manage their own API tokens + return true; + */ + return true; + } + + /** + * Determine whether the user can use root permissions for API tokens. + */ + public function useRootPermissions(User $user): bool + { + // Only admins and owners can use root permissions + return $user->isAdmin() || $user->isOwner(); + } + + /** + * Determine whether the user can use write permissions for API tokens. + */ + public function useWritePermissions(User $user): bool + { + // Authorization temporarily disabled + /* + // Only admins and owners can use write permissions + return $user->isAdmin() || $user->isOwner(); + */ + return true; + } +} diff --git a/app/Policies/ApplicationPolicy.php b/app/Policies/ApplicationPolicy.php index 62deb50b1..d64a436ad 100644 --- a/app/Policies/ApplicationPolicy.php +++ b/app/Policies/ApplicationPolicy.php @@ -13,6 +13,10 @@ class ApplicationPolicy */ public function viewAny(User $user): bool { + // Authorization temporarily disabled + /* + return true; + */ return true; } @@ -21,6 +25,10 @@ public function viewAny(User $user): bool */ public function view(User $user, Application $application): bool { + // Authorization temporarily disabled + /* + return true; + */ return true; } @@ -29,11 +37,15 @@ public function view(User $user, Application $application): bool */ public function create(User $user): bool { + // Authorization temporarily disabled + /* if ($user->isAdmin()) { return true; } return false; + */ + return true; } /** @@ -41,11 +53,15 @@ public function create(User $user): bool */ public function update(User $user, Application $application): Response { + // Authorization temporarily disabled + /* if ($user->isAdmin()) { return Response::allow(); } return Response::deny('As a member, you cannot update this application.

You need at least admin or owner permissions.'); + */ + return Response::allow(); } /** @@ -53,11 +69,15 @@ public function update(User $user, Application $application): Response */ public function delete(User $user, Application $application): bool { + // Authorization temporarily disabled + /* if ($user->isAdmin()) { return true; } return false; + */ + return true; } /** @@ -65,6 +85,10 @@ public function delete(User $user, Application $application): bool */ public function restore(User $user, Application $application): bool { + // Authorization temporarily disabled + /* + return true; + */ return true; } @@ -73,7 +97,11 @@ public function restore(User $user, Application $application): bool */ public function forceDelete(User $user, Application $application): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $application->team()->first()->id) !== null; + // Authorization temporarily disabled + /* + return $user->isAdmin() && $user->teams->contains('id', $application->team()->first()->id); + */ + return true; } /** @@ -81,7 +109,11 @@ public function forceDelete(User $user, Application $application): bool */ public function deploy(User $user, Application $application): bool { - return $user->teams()->get()->firstWhere('id', $application->team()->first()->id) !== null; + // Authorization temporarily disabled + /* + return $user->teams->contains('id', $application->team()->first()->id); + */ + return true; } /** @@ -89,7 +121,11 @@ public function deploy(User $user, Application $application): bool */ public function manageDeployments(User $user, Application $application): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $application->team()->first()->id) !== null; + // Authorization temporarily disabled + /* + return $user->isAdmin() && $user->teams->contains('id', $application->team()->first()->id); + */ + return true; } /** @@ -97,7 +133,11 @@ public function manageDeployments(User $user, Application $application): bool */ public function manageEnvironment(User $user, Application $application): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $application->team()->first()->id) !== null; + // Authorization temporarily disabled + /* + return $user->isAdmin() && $user->teams->contains('id', $application->team()->first()->id); + */ + return true; } /** @@ -105,6 +145,10 @@ public function manageEnvironment(User $user, Application $application): bool */ public function cleanupDeploymentQueue(User $user): bool { + // Authorization temporarily disabled + /* return $user->isAdmin(); + */ + return true; } } diff --git a/app/Policies/ApplicationPreviewPolicy.php b/app/Policies/ApplicationPreviewPolicy.php index 26cf13fcc..14efbdef9 100644 --- a/app/Policies/ApplicationPreviewPolicy.php +++ b/app/Policies/ApplicationPreviewPolicy.php @@ -21,7 +21,8 @@ public function viewAny(User $user): bool */ public function view(User $user, ApplicationPreview $applicationPreview): bool { - return $user->teams()->get()->firstWhere('id', $applicationPreview->application->team()->first()->id) !== null; + // return $user->teams->contains('id', $applicationPreview->application->team()->first()->id); + return true; } /** @@ -29,7 +30,8 @@ public function view(User $user, ApplicationPreview $applicationPreview): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -37,11 +39,12 @@ public function create(User $user): bool */ public function update(User $user, ApplicationPreview $applicationPreview): Response { - if ($user->isAdmin()) { - return Response::allow(); - } + // if ($user->isAdmin()) { + // return Response::allow(); + // } - return Response::deny('As a member, you cannot update this preview.

You need at least admin or owner permissions.'); + // return Response::deny('As a member, you cannot update this preview.

You need at least admin or owner permissions.'); + return true; } /** @@ -49,7 +52,8 @@ public function update(User $user, ApplicationPreview $applicationPreview): Resp */ public function delete(User $user, ApplicationPreview $applicationPreview): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $applicationPreview->application->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $applicationPreview->application->team()->first()->id); + return true; } /** @@ -57,7 +61,8 @@ public function delete(User $user, ApplicationPreview $applicationPreview): bool */ public function restore(User $user, ApplicationPreview $applicationPreview): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $applicationPreview->application->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $applicationPreview->application->team()->first()->id); + return true; } /** @@ -65,7 +70,8 @@ public function restore(User $user, ApplicationPreview $applicationPreview): boo */ public function forceDelete(User $user, ApplicationPreview $applicationPreview): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $applicationPreview->application->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $applicationPreview->application->team()->first()->id); + return true; } /** @@ -73,7 +79,8 @@ public function forceDelete(User $user, ApplicationPreview $applicationPreview): */ public function deploy(User $user, ApplicationPreview $applicationPreview): bool { - return $user->teams()->get()->firstWhere('id', $applicationPreview->application->team()->first()->id) !== null; + // return $user->teams->contains('id', $applicationPreview->application->team()->first()->id); + return true; } /** @@ -81,6 +88,7 @@ public function deploy(User $user, ApplicationPreview $applicationPreview): bool */ public function manageDeployments(User $user, ApplicationPreview $applicationPreview): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $applicationPreview->application->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $applicationPreview->application->team()->first()->id); + return true; } } diff --git a/app/Policies/ApplicationSettingPolicy.php b/app/Policies/ApplicationSettingPolicy.php index ff5e81d2f..848dc9aee 100644 --- a/app/Policies/ApplicationSettingPolicy.php +++ b/app/Policies/ApplicationSettingPolicy.php @@ -20,7 +20,8 @@ public function viewAny(User $user): bool */ public function view(User $user, ApplicationSetting $applicationSetting): bool { - return $user->teams()->get()->firstWhere('id', $applicationSetting->application->team()->first()->id) !== null; + // return $user->teams->contains('id', $applicationSetting->application->team()->first()->id); + return true; } /** @@ -28,7 +29,8 @@ public function view(User $user, ApplicationSetting $applicationSetting): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,7 +38,8 @@ public function create(User $user): bool */ public function update(User $user, ApplicationSetting $applicationSetting): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $applicationSetting->application->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $applicationSetting->application->team()->first()->id); + return true; } /** @@ -44,7 +47,8 @@ public function update(User $user, ApplicationSetting $applicationSetting): bool */ public function delete(User $user, ApplicationSetting $applicationSetting): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $applicationSetting->application->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $applicationSetting->application->team()->first()->id); + return true; } /** @@ -52,7 +56,8 @@ public function delete(User $user, ApplicationSetting $applicationSetting): bool */ public function restore(User $user, ApplicationSetting $applicationSetting): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $applicationSetting->application->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $applicationSetting->application->team()->first()->id); + return true; } /** @@ -60,6 +65,7 @@ public function restore(User $user, ApplicationSetting $applicationSetting): boo */ public function forceDelete(User $user, ApplicationSetting $applicationSetting): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $applicationSetting->application->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $applicationSetting->application->team()->first()->id); + return true; } } diff --git a/app/Policies/DatabasePolicy.php b/app/Policies/DatabasePolicy.php index b38dad923..520c0006e 100644 --- a/app/Policies/DatabasePolicy.php +++ b/app/Policies/DatabasePolicy.php @@ -20,7 +20,8 @@ public function viewAny(User $user): bool */ public function view(User $user, $database): bool { - return $user->teams()->get()->firstWhere('id', $database->team()->first()->id) !== null; + // return $user->teams->contains('id', $database->team()->first()->id); + return true; } /** @@ -28,7 +29,8 @@ public function view(User $user, $database): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,11 +38,12 @@ public function create(User $user): bool */ public function update(User $user, $database): Response { - if ($user->isAdmin() && $user->teams()->get()->firstWhere('id', $database->team()->first()->id) !== null) { - return Response::allow(); - } + // if ($user->isAdmin() && $user->teams->contains('id', $database->team()->first()->id)) { + // return Response::allow(); + // } - return Response::deny('As a member, you cannot update this database.

You need at least admin or owner permissions.'); + // return Response::deny('As a member, you cannot update this database.

You need at least admin or owner permissions.'); + return true; } /** @@ -48,7 +51,8 @@ public function update(User $user, $database): Response */ public function delete(User $user, $database): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $database->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $database->team()->first()->id); + return true; } /** @@ -56,7 +60,8 @@ public function delete(User $user, $database): bool */ public function restore(User $user, $database): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $database->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $database->team()->first()->id); + return true; } /** @@ -64,7 +69,8 @@ public function restore(User $user, $database): bool */ public function forceDelete(User $user, $database): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $database->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $database->team()->first()->id); + return true; } /** @@ -72,7 +78,8 @@ public function forceDelete(User $user, $database): bool */ public function manage(User $user, $database): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $database->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $database->team()->first()->id); + return true; } /** @@ -80,7 +87,8 @@ public function manage(User $user, $database): bool */ public function manageBackups(User $user, $database): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $database->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $database->team()->first()->id); + return true; } /** @@ -88,6 +96,7 @@ public function manageBackups(User $user, $database): bool */ public function manageEnvironment(User $user, $database): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $database->team()->first()->id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $database->team()->first()->id); + return true; } } diff --git a/app/Policies/EnvironmentPolicy.php b/app/Policies/EnvironmentPolicy.php index 9fdcb3abe..7199abb25 100644 --- a/app/Policies/EnvironmentPolicy.php +++ b/app/Policies/EnvironmentPolicy.php @@ -20,7 +20,8 @@ public function viewAny(User $user): bool */ public function view(User $user, Environment $environment): bool { - return $user->teams()->get()->firstWhere('id', $environment->project->team_id) !== null; + // return $user->teams->contains('id', $environment->project->team_id); + return true; } /** @@ -28,7 +29,8 @@ public function view(User $user, Environment $environment): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,7 +38,8 @@ public function create(User $user): bool */ public function update(User $user, Environment $environment): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $environment->project->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $environment->project->team_id); + return true; } /** @@ -44,7 +47,8 @@ public function update(User $user, Environment $environment): bool */ public function delete(User $user, Environment $environment): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $environment->project->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $environment->project->team_id); + return true; } /** @@ -52,7 +56,8 @@ public function delete(User $user, Environment $environment): bool */ public function restore(User $user, Environment $environment): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $environment->project->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $environment->project->team_id); + return true; } /** @@ -60,6 +65,7 @@ public function restore(User $user, Environment $environment): bool */ public function forceDelete(User $user, Environment $environment): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $environment->project->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $environment->project->team_id); + return true; } } diff --git a/app/Policies/GithubAppPolicy.php b/app/Policies/GithubAppPolicy.php new file mode 100644 index 000000000..56bec7032 --- /dev/null +++ b/app/Policies/GithubAppPolicy.php @@ -0,0 +1,79 @@ +teams->contains('id', $githubApp->team_id) || $githubApp->is_system_wide; + return true; + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + // return $user->isAdmin(); + return true; + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, GithubApp $githubApp): bool + { + if ($githubApp->is_system_wide) { + // return $user->isAdmin(); + return true; + } + + // return $user->isAdmin() && $user->teams->contains('id', $githubApp->team_id); + return true; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, GithubApp $githubApp): bool + { + if ($githubApp->is_system_wide) { + // return $user->isAdmin(); + return true; + } + + // return $user->isAdmin() && $user->teams->contains('id', $githubApp->team_id); + return true; + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, GithubApp $githubApp): bool + { + return false; + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, GithubApp $githubApp): bool + { + return false; + } +} diff --git a/app/Policies/NotificationPolicy.php b/app/Policies/NotificationPolicy.php new file mode 100644 index 000000000..4f3be431d --- /dev/null +++ b/app/Policies/NotificationPolicy.php @@ -0,0 +1,56 @@ +team) { + return false; + } + + // return $user->teams()->where('teams.id', $notificationSettings->team->id)->exists(); + return true; + } + + /** + * Determine whether the user can update the notification settings. + */ + public function update(User $user, Model $notificationSettings): bool + { + // Check if the notification settings belong to the user's current team + if (! $notificationSettings->team) { + return false; + } + + // Only owners and admins can update notification settings + // return $user->isAdmin() || $user->isOwner(); + return true; + } + + /** + * Determine whether the user can manage (create, update, delete) notification settings. + */ + public function manage(User $user, Model $notificationSettings): bool + { + // return $this->update($user, $notificationSettings); + return true; + } + + /** + * Determine whether the user can send test notifications. + */ + public function sendTest(User $user, Model $notificationSettings): bool + { + // return $this->update($user, $notificationSettings); + return true; + } +} diff --git a/app/Policies/PrivateKeyPolicy.php b/app/Policies/PrivateKeyPolicy.php index 6b9fd2171..996054c95 100644 --- a/app/Policies/PrivateKeyPolicy.php +++ b/app/Policies/PrivateKeyPolicy.php @@ -20,7 +20,8 @@ public function viewAny(User $user): bool */ public function view(User $user, PrivateKey $privateKey): bool { - return $user->teams()->get()->firstWhere('id', $privateKey->team_id) !== null; + // return $user->teams->contains('id', $privateKey->team_id); + return true; } /** @@ -28,7 +29,8 @@ public function view(User $user, PrivateKey $privateKey): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,7 +38,8 @@ public function create(User $user): bool */ public function update(User $user, PrivateKey $privateKey): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $privateKey->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $privateKey->team_id); + return true; } /** @@ -44,7 +47,8 @@ public function update(User $user, PrivateKey $privateKey): bool */ public function delete(User $user, PrivateKey $privateKey): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $privateKey->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $privateKey->team_id); + return true; } /** diff --git a/app/Policies/ProjectPolicy.php b/app/Policies/ProjectPolicy.php index 27ed159bd..e188c293f 100644 --- a/app/Policies/ProjectPolicy.php +++ b/app/Policies/ProjectPolicy.php @@ -20,7 +20,8 @@ public function viewAny(User $user): bool */ public function view(User $user, Project $project): bool { - return $user->teams()->get()->firstWhere('id', $project->team_id) !== null; + // return $user->teams->contains('id', $project->team_id); + return true; } /** @@ -28,7 +29,8 @@ public function view(User $user, Project $project): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,7 +38,8 @@ public function create(User $user): bool */ public function update(User $user, Project $project): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $project->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $project->team_id); + return true; } /** @@ -44,7 +47,8 @@ public function update(User $user, Project $project): bool */ public function delete(User $user, Project $project): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $project->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $project->team_id); + return true; } /** @@ -52,7 +56,8 @@ public function delete(User $user, Project $project): bool */ public function restore(User $user, Project $project): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $project->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $project->team_id); + return true; } /** @@ -60,6 +65,7 @@ public function restore(User $user, Project $project): bool */ public function forceDelete(User $user, Project $project): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $project->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $project->team_id); + return true; } } diff --git a/app/Policies/ResourceCreatePolicy.php b/app/Policies/ResourceCreatePolicy.php index 58f6ba212..9ed2b66ab 100644 --- a/app/Policies/ResourceCreatePolicy.php +++ b/app/Policies/ResourceCreatePolicy.php @@ -30,6 +30,7 @@ class ResourceCreatePolicy StandaloneClickhouse::class, Service::class, Application::class, + GithubApp::class, ]; /** @@ -37,7 +38,8 @@ class ResourceCreatePolicy */ public function createAny(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -49,7 +51,8 @@ public function create(User $user, string $resourceClass): bool return false; } - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** diff --git a/app/Policies/S3StoragePolicy.php b/app/Policies/S3StoragePolicy.php index 9b24dd31a..982c7c523 100644 --- a/app/Policies/S3StoragePolicy.php +++ b/app/Policies/S3StoragePolicy.php @@ -3,7 +3,6 @@ namespace App\Policies; use App\Models\S3Storage; -use App\Models\Server; use App\Models\User; class S3StoragePolicy @@ -21,7 +20,7 @@ public function viewAny(User $user): bool */ public function view(User $user, S3Storage $storage): bool { - return $user->teams()->get()->firstWhere('id', $storage->team_id)->exists(); + return $user->teams->contains('id', $storage->team_id); } /** @@ -35,9 +34,10 @@ public function create(User $user): bool /** * Determine whether the user can update the model. */ - public function update(User $user, Server $server): bool + public function update(User $user, S3Storage $storage): bool { - return $user->teams()->get()->firstWhere('id', $server->team_id)->exists() && $user->isAdmin(); + // return $user->teams->contains('id', $storage->team_id) && $user->isAdmin(); + return $user->teams->contains('id', $storage->team_id); } /** @@ -45,7 +45,8 @@ public function update(User $user, Server $server): bool */ public function delete(User $user, S3Storage $storage): bool { - return $user->teams()->get()->firstWhere('id', $storage->team_id)->exists() && $user->isAdmin(); + // return $user->teams->contains('id', $storage->team_id) && $user->isAdmin(); + return $user->teams->contains('id', $storage->team_id); } /** @@ -63,4 +64,12 @@ public function forceDelete(User $user, S3Storage $storage): bool { return false; } + + /** + * Determine whether the user can validate the connection of the model. + */ + public function validateConnection(User $user, S3Storage $storage): bool + { + return $user->teams->contains('id', $storage->team_id); + } } diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index a9a64e801..5cc6b739f 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -20,7 +20,7 @@ public function viewAny(User $user): bool */ public function view(User $user, Server $server): bool { - return $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + return $user->teams->contains('id', $server->team_id); } /** @@ -36,7 +36,7 @@ public function create(User $user): bool */ public function update(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + return $user->isAdmin() && $user->teams->contains('id', $server->team_id); } /** @@ -44,7 +44,7 @@ public function update(User $user, Server $server): bool */ public function delete(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + return $user->isAdmin() && $user->teams->contains('id', $server->team_id); } /** @@ -68,7 +68,7 @@ public function forceDelete(User $user, Server $server): bool */ public function manageProxy(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + return $user->isAdmin() && $user->teams->contains('id', $server->team_id); } /** @@ -76,7 +76,7 @@ public function manageProxy(User $user, Server $server): bool */ public function manageSentinel(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + return $user->isAdmin() && $user->teams->contains('id', $server->team_id); } /** @@ -84,7 +84,7 @@ public function manageSentinel(User $user, Server $server): bool */ public function manageCaCertificate(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + return $user->isAdmin() && $user->teams->contains('id', $server->team_id); } /** @@ -92,7 +92,7 @@ public function manageCaCertificate(User $user, Server $server): bool */ public function viewTerminal(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + return $user->isAdmin() && $user->teams->contains('id', $server->team_id); } /** @@ -100,6 +100,6 @@ public function viewTerminal(User $user, Server $server): bool */ public function viewSecurity(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + return $user->isAdmin() && $user->teams->contains('id', $server->team_id); } } diff --git a/app/Policies/ServiceApplicationPolicy.php b/app/Policies/ServiceApplicationPolicy.php new file mode 100644 index 000000000..af380a90f --- /dev/null +++ b/app/Policies/ServiceApplicationPolicy.php @@ -0,0 +1,63 @@ +service); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + // return $user->isAdmin(); + return true; + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, ServiceApplication $serviceApplication): bool + { + // return Gate::allows('update', $serviceApplication->service); + return true; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, ServiceApplication $serviceApplication): bool + { + // return Gate::allows('delete', $serviceApplication->service); + return true; + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, ServiceApplication $serviceApplication): bool + { + // return Gate::allows('update', $serviceApplication->service); + return true; + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, ServiceApplication $serviceApplication): bool + { + // return Gate::allows('delete', $serviceApplication->service); + return true; + } +} diff --git a/app/Policies/ServiceDatabasePolicy.php b/app/Policies/ServiceDatabasePolicy.php new file mode 100644 index 000000000..023434a24 --- /dev/null +++ b/app/Policies/ServiceDatabasePolicy.php @@ -0,0 +1,63 @@ +service); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + // return $user->isAdmin(); + return true; + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, ServiceDatabase $serviceDatabase): bool + { + // return Gate::allows('update', $serviceDatabase->service); + return true; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, ServiceDatabase $serviceDatabase): bool + { + // return Gate::allows('delete', $serviceDatabase->service); + return true; + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, ServiceDatabase $serviceDatabase): bool + { + // return Gate::allows('update', $serviceDatabase->service); + return true; + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, ServiceDatabase $serviceDatabase): bool + { + // return Gate::allows('delete', $serviceDatabase->service); + return true; + } +} diff --git a/app/Policies/ServicePolicy.php b/app/Policies/ServicePolicy.php index c3f1f9a9a..7ab0fe7d0 100644 --- a/app/Policies/ServicePolicy.php +++ b/app/Policies/ServicePolicy.php @@ -28,7 +28,8 @@ public function view(User $user, Service $service): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,7 +37,13 @@ public function create(User $user): bool */ public function update(User $user, Service $service): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null; + $team = $service->team(); + if (! $team) { + return false; + } + + // return $user->isAdmin() && $user->teams->contains('id', $team->id); + return true; } /** @@ -44,11 +51,12 @@ public function update(User $user, Service $service): bool */ public function delete(User $user, Service $service): bool { - if ($user->isAdmin()) { - return true; - } + // if ($user->isAdmin()) { + // return true; + // } - return false; + // return false; + return true; } /** @@ -56,6 +64,7 @@ public function delete(User $user, Service $service): bool */ public function restore(User $user, Service $service): bool { + // return true; return true; } @@ -64,16 +73,23 @@ public function restore(User $user, Service $service): bool */ public function forceDelete(User $user, Service $service): bool { - if ($user->isAdmin()) { - return true; - } + // if ($user->isAdmin()) { + // return true; + // } - return false; + // return false; + return true; } public function stop(User $user, Service $service): bool { - return $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null; + $team = $service->team(); + if (! $team) { + return false; + } + + // return $user->teams->contains('id', $team->id); + return true; } /** @@ -81,7 +97,13 @@ public function stop(User $user, Service $service): bool */ public function manageEnvironment(User $user, Service $service): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null; + $team = $service->team(); + if (! $team) { + return false; + } + + // return $user->isAdmin() && $user->teams->contains('id', $team->id); + return true; } /** @@ -89,6 +111,18 @@ public function manageEnvironment(User $user, Service $service): bool */ public function deploy(User $user, Service $service): bool { - return $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null; + $team = $service->team(); + if (! $team) { + return false; + } + + // return $user->teams->contains('id', $team->id); + return true; + } + + public function accessTerminal(User $user, Service $service): bool + { + // return $user->isAdmin() || $user->teams->contains('id', $service->team()->id); + return true; } } diff --git a/app/Policies/SharedEnvironmentVariablePolicy.php b/app/Policies/SharedEnvironmentVariablePolicy.php new file mode 100644 index 000000000..b465d8a0c --- /dev/null +++ b/app/Policies/SharedEnvironmentVariablePolicy.php @@ -0,0 +1,79 @@ +teams->contains('id', $sharedEnvironmentVariable->team_id); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + // return $user->isAdmin(); + return true; + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, SharedEnvironmentVariable $sharedEnvironmentVariable): bool + { + // return $user->isAdmin() && $user->teams->contains('id', $sharedEnvironmentVariable->team_id); + return true; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, SharedEnvironmentVariable $sharedEnvironmentVariable): bool + { + // return $user->isAdmin() && $user->teams->contains('id', $sharedEnvironmentVariable->team_id); + return true; + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, SharedEnvironmentVariable $sharedEnvironmentVariable): bool + { + // return $user->isAdmin() && $user->teams->contains('id', $sharedEnvironmentVariable->team_id); + return true; + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, SharedEnvironmentVariable $sharedEnvironmentVariable): bool + { + // return $user->isAdmin() && $user->teams->contains('id', $sharedEnvironmentVariable->team_id); + return true; + } + + /** + * Determine whether the user can manage environment variables. + */ + public function manageEnvironment(User $user, SharedEnvironmentVariable $sharedEnvironmentVariable): bool + { + // return $user->isAdmin() && $user->teams->contains('id', $sharedEnvironmentVariable->team_id); + return true; + } +} diff --git a/app/Policies/StandaloneDockerPolicy.php b/app/Policies/StandaloneDockerPolicy.php index 08d7ea6fe..154648599 100644 --- a/app/Policies/StandaloneDockerPolicy.php +++ b/app/Policies/StandaloneDockerPolicy.php @@ -20,7 +20,7 @@ public function viewAny(User $user): bool */ public function view(User $user, StandaloneDocker $standaloneDocker): bool { - return $user->teams()->get()->firstWhere('id', $standaloneDocker->server->team_id) !== null; + return $user->teams->contains('id', $standaloneDocker->server->team_id); } /** @@ -28,7 +28,8 @@ public function view(User $user, StandaloneDocker $standaloneDocker): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,7 +37,8 @@ public function create(User $user): bool */ public function update(User $user, StandaloneDocker $standaloneDocker): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $standaloneDocker->server->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $standaloneDocker->server->team_id); + return true; } /** @@ -44,7 +46,8 @@ public function update(User $user, StandaloneDocker $standaloneDocker): bool */ public function delete(User $user, StandaloneDocker $standaloneDocker): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $standaloneDocker->server->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $standaloneDocker->server->team_id); + return true; } /** @@ -52,7 +55,8 @@ public function delete(User $user, StandaloneDocker $standaloneDocker): bool */ public function restore(User $user, StandaloneDocker $standaloneDocker): bool { - return false; + // return false; + return true; } /** @@ -60,6 +64,7 @@ public function restore(User $user, StandaloneDocker $standaloneDocker): bool */ public function forceDelete(User $user, StandaloneDocker $standaloneDocker): bool { - return false; + // return false; + return true; } } diff --git a/app/Policies/SwarmDockerPolicy.php b/app/Policies/SwarmDockerPolicy.php index 94ea7a1ee..979bb5889 100644 --- a/app/Policies/SwarmDockerPolicy.php +++ b/app/Policies/SwarmDockerPolicy.php @@ -20,7 +20,7 @@ public function viewAny(User $user): bool */ public function view(User $user, SwarmDocker $swarmDocker): bool { - return $user->teams()->get()->firstWhere('id', $swarmDocker->server->team_id) !== null; + return $user->teams->contains('id', $swarmDocker->server->team_id); } /** @@ -28,7 +28,8 @@ public function view(User $user, SwarmDocker $swarmDocker): bool */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,7 +37,8 @@ public function create(User $user): bool */ public function update(User $user, SwarmDocker $swarmDocker): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $swarmDocker->server->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $swarmDocker->server->team_id); + return true; } /** @@ -44,7 +46,8 @@ public function update(User $user, SwarmDocker $swarmDocker): bool */ public function delete(User $user, SwarmDocker $swarmDocker): bool { - return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $swarmDocker->server->team_id) !== null; + // return $user->isAdmin() && $user->teams->contains('id', $swarmDocker->server->team_id); + return true; } /** @@ -52,7 +55,8 @@ public function delete(User $user, SwarmDocker $swarmDocker): bool */ public function restore(User $user, SwarmDocker $swarmDocker): bool { - return false; + // return false; + return true; } /** @@ -60,6 +64,7 @@ public function restore(User $user, SwarmDocker $swarmDocker): bool */ public function forceDelete(User $user, SwarmDocker $swarmDocker): bool { - return false; + // return false; + return true; } } diff --git a/app/Policies/TeamPolicy.php b/app/Policies/TeamPolicy.php new file mode 100644 index 000000000..b7ef48943 --- /dev/null +++ b/app/Policies/TeamPolicy.php @@ -0,0 +1,104 @@ +teams->contains('id', $team->id); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + // All authenticated users can create teams + return true; + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, Team $team): bool + { + // Only admins and owners can update team settings + if (! $user->teams->contains('id', $team->id)) { + return false; + } + + // return $user->isAdmin() || $user->isOwner(); + return true; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, Team $team): bool + { + // Only admins and owners can delete teams + if (! $user->teams->contains('id', $team->id)) { + return false; + } + + // return $user->isAdmin() || $user->isOwner(); + return true; + } + + /** + * Determine whether the user can manage team members. + */ + public function manageMembers(User $user, Team $team): bool + { + // Only admins and owners can manage team members + if (! $user->teams->contains('id', $team->id)) { + return false; + } + + // return $user->isAdmin() || $user->isOwner(); + return true; + } + + /** + * Determine whether the user can view admin panel. + */ + public function viewAdmin(User $user, Team $team): bool + { + // Only admins and owners can view admin panel + if (! $user->teams->contains('id', $team->id)) { + return false; + } + + // return $user->isAdmin() || $user->isOwner(); + return true; + } + + /** + * Determine whether the user can manage invitations. + */ + public function manageInvitations(User $user, Team $team): bool + { + // Only admins and owners can manage invitations + if (! $user->teams->contains('id', $team->id)) { + return false; + } + + // return $user->isAdmin() || $user->isOwner(); + return true; + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 50d21b724..a17d9ec5e 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -23,8 +23,11 @@ class AuthServiceProvider extends ServiceProvider \App\Models\ApplicationPreview::class => \App\Policies\ApplicationPreviewPolicy::class, \App\Models\ApplicationSetting::class => \App\Policies\ApplicationSettingPolicy::class, \App\Models\Service::class => \App\Policies\ServicePolicy::class, + \App\Models\ServiceApplication::class => \App\Policies\ServiceApplicationPolicy::class, + \App\Models\ServiceDatabase::class => \App\Policies\ServiceDatabasePolicy::class, \App\Models\Project::class => \App\Policies\ProjectPolicy::class, \App\Models\Environment::class => \App\Policies\EnvironmentPolicy::class, + \App\Models\SharedEnvironmentVariable::class => \App\Policies\SharedEnvironmentVariablePolicy::class, // Database policies - all use the shared DatabasePolicy \App\Models\StandalonePostgresql::class => \App\Policies\DatabasePolicy::class, \App\Models\StandaloneMysql::class => \App\Policies\DatabasePolicy::class, @@ -35,6 +38,22 @@ class AuthServiceProvider extends ServiceProvider \App\Models\StandaloneDragonfly::class => \App\Policies\DatabasePolicy::class, \App\Models\StandaloneClickhouse::class => \App\Policies\DatabasePolicy::class, + // Notification policies - all use the shared NotificationPolicy + \App\Models\EmailNotificationSettings::class => \App\Policies\NotificationPolicy::class, + \App\Models\DiscordNotificationSettings::class => \App\Policies\NotificationPolicy::class, + \App\Models\TelegramNotificationSettings::class => \App\Policies\NotificationPolicy::class, + \App\Models\SlackNotificationSettings::class => \App\Policies\NotificationPolicy::class, + \App\Models\PushoverNotificationSettings::class => \App\Policies\NotificationPolicy::class, + + // API Token policy + \Laravel\Sanctum\PersonalAccessToken::class => \App\Policies\ApiTokenPolicy::class, + + // Team policy + \App\Models\Team::class => \App\Policies\TeamPolicy::class, + + // Git source policies + \App\Models\GithubApp::class => \App\Policies\GithubAppPolicy::class, + ]; /** @@ -44,5 +63,11 @@ public function boot(): void { // Register gates for resource creation policy Gate::define('createAnyResource', [ResourceCreatePolicy::class, 'createAny']); + + // Register gate for terminal access + Gate::define('canAccessTerminal', function ($user) { + // return $user->isAdmin() || $user->isOwner(); + return true; + }); } } diff --git a/app/View/Components/Forms/Button.php b/app/View/Components/Forms/Button.php index bf88d3f88..b54444261 100644 --- a/app/View/Components/Forms/Button.php +++ b/app/View/Components/Forms/Button.php @@ -4,6 +4,7 @@ use Closure; use Illuminate\Contracts\View\View; +use Illuminate\Support\Facades\Gate; use Illuminate\View\Component; class Button extends Component @@ -17,7 +18,19 @@ public function __construct( public ?string $modalId = null, public string $defaultClass = 'button', public bool $showLoadingIndicator = true, + public ?string $canGate = null, + public mixed $canResource = null, + public bool $autoDisable = true, ) { + // Handle authorization-based disabling + if ($this->canGate && $this->canResource && $this->autoDisable) { + $hasPermission = Gate::allows($this->canGate, $this->canResource); + + if (! $hasPermission) { + $this->disabled = true; + } + } + if ($this->noStyle) { $this->defaultClass = ''; } diff --git a/app/View/Components/Forms/Checkbox.php b/app/View/Components/Forms/Checkbox.php index 8db739642..ece7f0e35 100644 --- a/app/View/Components/Forms/Checkbox.php +++ b/app/View/Components/Forms/Checkbox.php @@ -4,6 +4,7 @@ use Closure; use Illuminate\Contracts\View\View; +use Illuminate\Support\Facades\Gate; use Illuminate\View\Component; class Checkbox extends Component @@ -22,7 +23,20 @@ public function __construct( public string|bool $instantSave = false, public bool $disabled = false, public string $defaultClass = 'dark:border-neutral-700 text-coolgray-400 focus:ring-warning dark:bg-coolgray-100 rounded-sm cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed', + public ?string $canGate = null, + public mixed $canResource = null, + public bool $autoDisable = true, ) { + // Handle authorization-based disabling + if ($this->canGate && $this->canResource && $this->autoDisable) { + $hasPermission = Gate::allows($this->canGate, $this->canResource); + + if (! $hasPermission) { + $this->disabled = true; + $this->instantSave = false; // Disable instant save for unauthorized users + } + } + if ($this->disabled) { $this->defaultClass .= ' opacity-40'; } diff --git a/app/View/Components/Forms/Input.php b/app/View/Components/Forms/Input.php index a7bd87949..83c98c0df 100644 --- a/app/View/Components/Forms/Input.php +++ b/app/View/Components/Forms/Input.php @@ -4,6 +4,7 @@ use Closure; use Illuminate\Contracts\View\View; +use Illuminate\Support\Facades\Gate; use Illuminate\View\Component; use Visus\Cuid2\Cuid2; @@ -26,7 +27,19 @@ public function __construct( public ?int $minlength = null, public ?int $maxlength = null, public bool $autofocus = false, - ) {} + public ?string $canGate = null, + public mixed $canResource = null, + public bool $autoDisable = true, + ) { + // Handle authorization-based disabling + if ($this->canGate && $this->canResource && $this->autoDisable) { + $hasPermission = Gate::allows($this->canGate, $this->canResource); + + if (! $hasPermission) { + $this->disabled = true; + } + } + } public function render(): View|Closure|string { diff --git a/app/View/Components/Forms/Select.php b/app/View/Components/Forms/Select.php index feb4bf343..49b69136b 100644 --- a/app/View/Components/Forms/Select.php +++ b/app/View/Components/Forms/Select.php @@ -4,6 +4,7 @@ use Closure; use Illuminate\Contracts\View\View; +use Illuminate\Support\Facades\Gate; use Illuminate\View\Component; use Visus\Cuid2\Cuid2; @@ -19,9 +20,19 @@ public function __construct( public ?string $helper = null, public bool $required = false, public bool $disabled = false, - public string $defaultClass = 'select w-full' + public string $defaultClass = 'select w-full', + public ?string $canGate = null, + public mixed $canResource = null, + public bool $autoDisable = true, ) { - // + // Handle authorization-based disabling + if ($this->canGate && $this->canResource && $this->autoDisable) { + $hasPermission = Gate::allows($this->canGate, $this->canResource); + + if (! $hasPermission) { + $this->disabled = true; + } + } } /** diff --git a/app/View/Components/Forms/Textarea.php b/app/View/Components/Forms/Textarea.php index 6081c2a8a..3148d2566 100644 --- a/app/View/Components/Forms/Textarea.php +++ b/app/View/Components/Forms/Textarea.php @@ -4,6 +4,7 @@ use Closure; use Illuminate\Contracts\View\View; +use Illuminate\Support\Facades\Gate; use Illuminate\View\Component; use Visus\Cuid2\Cuid2; @@ -33,8 +34,18 @@ public function __construct( public string $defaultClassInput = 'input', public ?int $minlength = null, public ?int $maxlength = null, + public ?string $canGate = null, + public mixed $canResource = null, + public bool $autoDisable = true, ) { - // + // Handle authorization-based disabling + if ($this->canGate && $this->canResource && $this->autoDisable) { + $hasPermission = Gate::allows($this->canGate, $this->canResource); + + if (! $hasPermission) { + $this->disabled = true; + } + } } /** diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index 1a145aa4b..7ec7e4d4c 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -59,20 +59,20 @@ if (this.zoom === '90') { const style = document.createElement('style'); style.textContent = ` - html { - font-size: 93.75%; - } - - :root { - --vh: 1vh; - } - - @media (min-width: 1024px) { - html { - font-size: 87.5%; - } - } - `; + html { + font-size: 93.75%; + } + + :root { + --vh: 1vh; + } + + @media (min-width: 1024px) { + html { + font-size: 87.5%; + } + } + `; document.head.appendChild(style); } } @@ -229,20 +229,22 @@ class="{{ request()->is('tags*') ? 'menu-item-active menu-item' : 'menu-item' }} Tags -
  • - - - - - - - Terminal - -
  • + @can('canAccessTerminal') +
  • + + + + + + + Terminal + +
  • + @endcan
  • Destinations

    @if ($servers->count() > 0) - - - + @can('createAnyResource') + + + + @endcan @endif
    Network endpoints to deploy your resources.
    diff --git a/resources/views/livewire/destination/new/docker.blade.php b/resources/views/livewire/destination/new/docker.blade.php index 1502f70af..795b33e81 100644 --- a/resources/views/livewire/destination/new/docker.blade.php +++ b/resources/views/livewire/destination/new/docker.blade.php @@ -1,18 +1,25 @@ -
    -
    Destinations are used to segregate resources by network.
    -
    -
    - - -
    - - - @foreach ($servers as $server) - - @endforeach - - - Continue - -
    -
    +@can('createAnyResource') +
    +
    Destinations are used to segregate resources by network.
    +
    +
    + + +
    + + + @foreach ($servers as $server) + + @endforeach + + + Continue + +
    +
    +@else +
    +

    You don't have permission to create new destinations.

    +

    Please contact your team administrator for access.

    +
    +@endcan diff --git a/resources/views/livewire/destination/show.blade.php b/resources/views/livewire/destination/show.blade.php index 6c3a9d9dd..f12388770 100644 --- a/resources/views/livewire/destination/show.blade.php +++ b/resources/views/livewire/destination/show.blade.php @@ -2,14 +2,14 @@

    Destination

    - - Save - + Save @if ($network !== 'coolify') + shortConfirmationLabel="Destination Name" :confirmWithPassword="false" step2ButtonText="Permanently Delete" + canGate="delete" :canResource="$destination" /> @endif
    @@ -19,7 +19,7 @@
    A swarm Docker network. WIP
    @endif
    - + @if ($destination->getMorphClass() === 'App\Models\StandaloneDocker') diff --git a/resources/views/livewire/notifications/discord.blade.php b/resources/views/livewire/notifications/discord.blade.php index 4118af5f1..dbf56b027 100644 --- a/resources/views/livewire/notifications/discord.blade.php +++ b/resources/views/livewire/notifications/discord.blade.php @@ -6,27 +6,27 @@

    Discord

    - + Save @if ($discordEnabled) - Send Test Notification @else - + Send Test Notification @endif
    - - +
    - @@ -38,11 +38,11 @@

    Deployments

    - - -
    @@ -50,35 +50,35 @@

    Backups

    - -

    Scheduled Tasks

    - -

    Server

    - - - - - -
    diff --git a/resources/views/livewire/notifications/email.blade.php b/resources/views/livewire/notifications/email.blade.php index 74380f2cd..a3b46fc89 100644 --- a/resources/views/livewire/notifications/email.blade.php +++ b/resources/views/livewire/notifications/email.blade.php @@ -6,41 +6,43 @@

    Email

    - + Save @if (auth()->user()->isAdminFromSession()) - @if ($team->isNotificationEnabled('email')) - - - - - Send Email - - - - @else - - Send Test Email - - @endif + @can('sendTest', $settings) + @if ($team->isNotificationEnabled('email')) + +
    + + + Send Email + + +
    + @else + + Send Test Email + + @endif + @endcan @endif
    @if (!isCloud())
    -
    @endif @if (!$useInstanceEmailSettings)
    - - +
    @if (isInstanceAdmin() && !$useInstanceEmailSettings) - + Copy from Instance Settings @endif @@ -48,7 +50,7 @@ @if (isCloud())
    -
    @endif @@ -58,29 +60,29 @@ class="p-4 border dark:border-coolgray-300 border-neutral-200 flex flex-col gap-2">

    SMTP Server

    - + Save
    -
    - - - + + +
    - - - + +
    @@ -90,18 +92,18 @@ class="p-4 border dark:border-coolgray-300 border-neutral-200 flex flex-col gap- class="p-4 border dark:border-coolgray-300 border-neutral-200 flex flex-col gap-2">

    Resend

    - + Save
    -
    -
    @@ -117,11 +119,11 @@ class="p-4 border dark:border-coolgray-300 border-neutral-200 flex flex-col gap-

    Deployments

    - - -
    @@ -129,35 +131,35 @@ class="p-4 border dark:border-coolgray-300 border-neutral-200 flex flex-col gap-

    Backups

    - -

    Scheduled Tasks

    - -

    Server

    - - - - - -
    diff --git a/resources/views/livewire/notifications/pushover.blade.php b/resources/views/livewire/notifications/pushover.blade.php index f0123a070..8c967030f 100644 --- a/resources/views/livewire/notifications/pushover.blade.php +++ b/resources/views/livewire/notifications/pushover.blade.php @@ -6,28 +6,28 @@

    Pushover

    - + Save @if ($pushoverEnabled) - Send Test Notification @else - + Send Test Notification @endif
    - +
    - -
    @@ -40,11 +40,11 @@

    Deployments

    - - -
    @@ -52,35 +52,35 @@

    Backups

    - -

    Scheduled Tasks

    - -

    Server

    - - - - - -
    diff --git a/resources/views/livewire/notifications/slack.blade.php b/resources/views/livewire/notifications/slack.blade.php index 22c145460..ce4dd5d2d 100644 --- a/resources/views/livewire/notifications/slack.blade.php +++ b/resources/views/livewire/notifications/slack.blade.php @@ -6,24 +6,24 @@

    Slack

    - + Save @if ($slackEnabled) - Send Test Notification @else - + Send Test Notification @endif
    - +
    - @@ -35,11 +35,11 @@

    Deployments

    - - -
    @@ -47,33 +47,33 @@

    Backups

    - - + +

    Scheduled Tasks

    - -

    Server

    - - - - - - +
    diff --git a/resources/views/livewire/notifications/telegram.blade.php b/resources/views/livewire/notifications/telegram.blade.php index d4312f8d2..7b07b4e22 100644 --- a/resources/views/livewire/notifications/telegram.blade.php +++ b/resources/views/livewire/notifications/telegram.blade.php @@ -6,28 +6,28 @@

    Telegram

    - + Save @if ($telegramEnabled) - Send Test Notification @else - + Send Test Notification @endif
    - +
    - -
    @@ -42,27 +42,27 @@
    -
    -
    -
    -
    -
    -
    @@ -72,19 +72,19 @@
    -
    -
    -
    -
    @@ -95,19 +95,19 @@
    -
    -
    -
    -
    @@ -118,55 +118,55 @@
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/resources/views/livewire/project/application/advanced.blade.php b/resources/views/livewire/project/application/advanced.blade.php index df2b49d48..6dd5c872c 100644 --- a/resources/views/livewire/project/application/advanced.blade.php +++ b/resources/views/livewire/project/application/advanced.blade.php @@ -8,76 +8,73 @@

    General

    @if ($application->git_based()) + id="isAutoDeployEnabled" label="Auto Deploy" canGate="update" :canResource="$application" /> + instantSave id="isPreviewDeploymentsEnabled" label="Preview Deployments" canGate="update" + :canResource="$application" /> @endif - + @if ($application->settings->is_container_label_readonly_enabled) + instantSave id="isForceHttpsEnabled" label="Force Https" canGate="update" :canResource="$application" /> + instantSave id="isGzipEnabled" canGate="update" :canResource="$application" /> + instantSave id="isStripprefixEnabled" label="Strip Prefixes" canGate="update" :canResource="$application" /> @else + id="isForceHttpsEnabled" label="Force Https" canGate="update" :canResource="$application" /> + id="isGzipEnabled" canGate="update" :canResource="$application" /> + instantSave id="isStripprefixEnabled" label="Strip Prefixes" canGate="update" :canResource="$application" /> @endif @if ($application->build_pack === 'dockercompose')

    Docker Compose

    + helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the
    documentation." + canGate="update" :canResource="$application" /> @endif

    Container Names

    + instantSave id="isConsistentContainerNameEnabled" label="Consistent Container Names" canGate="update" + :canResource="$application" /> @if ($isConsistentContainerNameEnabled === false) - @can('update', $application) - - Save - - @else - - Save - - @endcan + instantSave id="customInternalName" label="Custom Container Name" canGate="update" + :canResource="$application" /> + Save @endif @if ($application->build_pack === 'dockercompose')

    Network

    + helper="By default, you do not reach the Coolify defined networks.
    Starting a docker compose based resource will have an internal network.
    If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.

    For more information, check this." + canGate="update" :canResource="$application" /> @endif

    Logs

    + instantSave id="isLogDrainEnabled" label="Drain Logs" canGate="update" :canResource="$application" /> @if ($application->git_based())

    Git

    + helper="Allow Git Submodules during build process." canGate="update" :canResource="$application" /> + helper="Allow Git LFS during build process." canGate="update" :canResource="$application" /> + helper="Use shallow cloning (--depth=1) to speed up deployments by only fetching the latest commit history. This reduces clone time and resource usage, especially for large repositories." + canGate="update" :canResource="$application" /> @endif
    @@ -87,16 +84,7 @@

    GPU

    @if ($isGpuEnabled) - @can('update', $application) - - Save - - @else - - Save - - @endcan + Save @endif
    @endif @@ -104,21 +92,23 @@
    + instantSave id="isGpuEnabled" label="Enable GPU" canGate="update" :canResource="$application" />
    @endif @if ($isGpuEnabled)
    - - + + +
    - - + id="gpuDeviceIds" canGate="update" :canResource="$application"> +
    @endif diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index b473286f0..b833fc7bb 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -1,56 +1,35 @@ -
    +

    General

    - @can('update', $application) - - Save - - @else - - Save - - @endcan - - {{-- - Download Config - --}} - {{-- - - --}} - + Save
    General configuration for your application.
    - - + +
    @if (!$application->dockerfile && $application->build_pack !== 'dockerimage')
    - @can('update', $application) - - - - - - - @else - - - - - - - @endcan + + + + + + @if ($application->settings->is_static || $application->build_pack === 'static') - + @@ -69,7 +48,8 @@ + id="parsedServiceDomains.{{ str($serviceName)->slug('_') }}.domain" + x-bind:disabled="shouldDisable()"> @can('update', $application) Generate Domain @@ -85,27 +65,23 @@ @if ($application->settings->is_static || $application->build_pack === 'static') + helper="You can add custom Nginx configuration here." x-bind:disabled="!canUpdate" /> @can('update', $application) Generate Default Nginx Configuration - @else - - Generate Default Nginx Configuration - @endcan @endif
    @if ($application->could_set_build_commands()) + helper="If your application is a static site or the final build assets should be served as a static site, enable this." + x-bind:disabled="!canUpdate" /> @endif @if ($application->settings->is_static && $application->build_pack !== 'static') + helper="If your application is a SPA, enable this." id="application.settings.is_spa" instantSave + x-bind:disabled="!canUpdate"> @endif
    @if ($application->build_pack !== 'dockercompose') @@ -113,11 +89,13 @@ @if ($application->settings->is_container_label_readonly_enabled == false) + helper="Readonly labels are disabled. You can set the domains in the labels section." + x-bind:disabled="!canUpdate" /> @else + helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.

    Example
    - http://app.coolify.io,https://cloud.coolify.io/dashboard
    - http://app.coolify.io/api/v3
    - http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. " + x-bind:disabled="!canUpdate" /> @can('update', $application) Generate Domain @@ -128,32 +106,38 @@ @if ($application->settings->is_container_label_readonly_enabled == false) @if ($application->redirect === 'both') + helper="Readonly labels are disabled. You can set the direction in the labels section." + x-bind:disabled="!canUpdate" /> @elseif ($application->redirect === 'www') + helper="Readonly labels are disabled. You can set the direction in the labels section." + x-bind:disabled="!canUpdate" /> @elseif ($application->redirect === 'non-www') + helper="Readonly labels are disabled. You can set the direction in the labels section." + x-bind:disabled="!canUpdate" /> @endif @else + helper="You must need to add www and non-www as an A DNS record. Make sure the www domain is added under Domains." + x-bind:disabled="!canUpdate"> @if ($application->settings->is_container_label_readonly_enabled) - - -
    Set Direction
    -
    -
    + @can('update', $application) + + +
    Set Direction
    +
    +
    + @endcan @endif @endif
    @@ -177,11 +161,15 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
    @if ($application->build_pack === 'dockerimage') @if ($application->destination->server->isSwarm()) - - + + @else - - + + @endif @else @if ( @@ -189,19 +177,20 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" $application->additional_servers->count() > 0 || $application->settings->is_build_server_enabled) + placeholder="Required!" x-bind:disabled="!canUpdate" /> + placeholder="Empty means latest will be used." label="Docker Image Tag" + x-bind:disabled="!canUpdate" /> @else + label="Docker Image" x-bind:disabled="!canUpdate" /> + label="Docker Image Tag" x-bind:disabled="!canUpdate" /> @endif @endif
    @@ -212,17 +201,21 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" + id="application.custom_docker_run_options" label="Custom Docker Options" + x-bind:disabled="!canUpdate" /> @else @if ($application->could_set_build_commands()) @if ($application->build_pack === 'nixpacks')
    + id="application.install_command" label="Install Command" + x-bind:disabled="!canUpdate" /> + id="application.build_command" label="Build Command" + x-bind:disabled="!canUpdate" /> + id="application.start_command" label="Start Command" + x-bind:disabled="!canUpdate" />
    Nixpacks will detect the required configuration automatically. @@ -240,10 +233,10 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
    @endcan
    - - @@ -252,7 +245,8 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" + helper="Git repository (based on the base directory settings) will be copied to the deployment directory." + x-bind:disabled="shouldDisable()" />
    The following commands are for advanced use cases. Only @@ -260,13 +254,13 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" know what are you doing.
    - - @@ -276,25 +270,28 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
    + helper="Directory to use as root. Useful for monorepos." + x-bind:disabled="!canUpdate" /> @if ($application->build_pack === 'dockerfile' && !$application->dockerfile) + helper="It is calculated together with the Base Directory:
    {{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}" + x-bind:disabled="!canUpdate" /> @endif @if ($application->build_pack === 'dockerfile') + helper="Useful if you have multi-staged dockerfile." + x-bind:disabled="!canUpdate" /> @endif @if ($application->could_set_build_commands()) @if ($application->settings->is_static) + label="Publish Directory" required x-bind:disabled="!canUpdate" /> @else + label="Publish Directory" x-bind:disabled="!canUpdate" /> @endif @endif @@ -304,20 +301,21 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" + label="Watch Paths" x-bind:disabled="!canUpdate" />
    @endif + id="application.custom_docker_run_options" label="Custom Docker Options" + x-bind:disabled="!canUpdate" /> @if ($application->build_pack !== 'dockercompose')
    + label="Use a Build Server?" x-bind:disabled="!canUpdate" />
    @endif @endif @@ -327,8 +325,10 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" @if ($application->build_pack === 'dockercompose')

    Docker Compose

    - Reload Compose File + @can('update', $application) + Reload Compose File + @endcan
    @if ($application->settings->is_raw_compose_deployment_enabled) + id="application.settings.is_container_label_escape_enabled" instantSave + x-bind:disabled="!canUpdate"> {{-- --}} @@ -358,32 +359,36 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" @endif @if ($application->dockerfile) + useMonacoEditor rows="6" x-bind:disabled="!canUpdate"> @endif @if ($application->build_pack !== 'dockercompose')

    Network

    @if ($application->settings->is_static || $application->build_pack === 'static') - + @else @if ($application->settings->is_container_label_readonly_enabled === false) + helper="Readonly labels are disabled. You can set the ports manually in the labels section." + x-bind:disabled="!canUpdate" /> @else + helper="A comma separated list of ports your application uses. The first port will be used as default healthcheck port if nothing defined in the Healthcheck menu. Be sure to set this correctly." + x-bind:disabled="!canUpdate" /> @endif @endif @if (!$application->destination->server->isSwarm()) + helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.

    Example:
    3000:3000,3002:3002

    Rolling update is not supported if you have a port mapped to the host." + x-bind:disabled="!canUpdate" /> @endif @if (!$application->destination->server->isSwarm()) + wire:model="application.custom_network_aliases" x-bind:disabled="!canUpdate" /> @endif
    @@ -391,60 +396,66 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
    + label="Enable" id="application.is_http_basic_auth_enabled" + x-bind:disabled="!canUpdate" />
    @if ($application->is_http_basic_auth_enabled)
    - + + required x-bind:disabled="!canUpdate" />
    @endif
    @if ($application->settings->is_container_label_readonly_enabled) + monacoEditorLanguage="ini" useMonacoEditor x-bind:disabled="!canUpdate"> @else + monacoEditorLanguage="ini" useMonacoEditor x-bind:disabled="!canUpdate"> @endif
    + id="application.settings.is_container_label_readonly_enabled" instantSave + x-bind:disabled="!canUpdate"> + id="application.settings.is_container_label_escape_enabled" instantSave + x-bind:disabled="!canUpdate">
    - + @can('update', $application) + + @endcan @endif

    Pre/Post Deployment Commands

    - @if ($application->build_pack === 'dockercompose') - @endif
    - @if ($application->build_pack === 'dockercompose') - @endif diff --git a/resources/views/livewire/project/application/heading.blade.php b/resources/views/livewire/project/application/heading.blade.php index 2b44e0ddd..1a8812929 100644 --- a/resources/views/livewire/project/application/heading.blade.php +++ b/resources/views/livewire/project/application/heading.blade.php @@ -15,10 +15,12 @@ Logs @if (!$application->destination->server->isSwarm()) - - Terminal - + @can('canAccessTerminal') + + Terminal + + @endcan @endif diff --git a/resources/views/livewire/project/application/preview/form.blade.php b/resources/views/livewire/project/application/preview/form.blade.php index a273de810..e00d824ef 100644 --- a/resources/views/livewire/project/application/preview/form.blade.php +++ b/resources/views/livewire/project/application/preview/form.blade.php @@ -1,13 +1,15 @@

    Preview Deployments

    - Save - Reset template to default + @can('update', $application) + Save + Reset template to default + @endcan
    Preview Deployments based on pull requests are here.
    + helper="Templates:
    @@{{ random }} to generate random sub-domain each time a PR is deployed
    @@{{ pr_id }} to use pull request ID as sub-domain or @@{{ domain }} to replace the domain name with the application's domain name." canGate="update" :canResource="$application" /> @if ($previewUrlTemplate)
    Domain Preview: {{ $previewUrlTemplate }}
    @endif diff --git a/resources/views/livewire/project/application/previews-compose.blade.php b/resources/views/livewire/project/application/previews-compose.blade.php index 4e50ad18d..ffed66814 100644 --- a/resources/views/livewire/project/application/previews-compose.blade.php +++ b/resources/views/livewire/project/application/previews-compose.blade.php @@ -1,6 +1,6 @@ + id="service.domain" canGate="update" :canResource="$preview->application">
    Save Generate Domain diff --git a/resources/views/livewire/project/application/previews.blade.php b/resources/views/livewire/project/application/previews.blade.php index 5decc26cf..6a2ef485f 100644 --- a/resources/views/livewire/project/application/previews.blade.php +++ b/resources/views/livewire/project/application/previews.blade.php @@ -7,9 +7,11 @@ class="dark:text-warning">{{ $application->destination->server->name }}.<
    @if ($application->is_github_based())
    -

    Pull Requests on Git

    - Load Pull Requests - + @can('update', $application) +

    Pull Requests on Git

    + Load Pull Requests + + @endcan
    @endif @isset($rate_limit_remaining) @@ -40,19 +42,23 @@ class="dark:text-warning">{{ $application->destination->server->name }}.< - - Configure - - - - - - Deploy - + @can('update', $application) + + Configure + + @endcan + @can('deploy', $application) + + + + + Deploy + + @endcan @endforeach @@ -106,10 +112,12 @@ class="dark:text-warning">{{ $application->destination->server->name }}.< - Save - Generate - Domain + id="application.previews.{{ $previewName }}.fqdn" canGate="update" :canResource="$application"> + @can('update', $application) + Save + Generate + Domain + @endcan @else @foreach (collect(json_decode($preview->docker_compose_domains)) as $serviceName => $service) @@ -122,82 +130,90 @@ class="flex items-end gap-2 pt-4"> @else
    - Save - Generate - Domain + id="application.previews.{{ $previewName }}.fqdn" canGate="update" :canResource="$application"> + @can('update', $application) + Save + Generate + Domain + @endcan
    @endif
    - - - - - - - - - Force deploy (without - cache) - - - @if (data_get($preview, 'status') === 'exited') - + @can('deploy', $application) + + - - - Deploy - @else - - - - - Redeploy - @endif - - @if (data_get($preview, 'status') !== 'exited') - - - + + + + + Force deploy (without + cache) + + + @if (data_get($preview, 'status') === 'exited') + + + + + Deploy + @else + + d="M10.09 4.01l.496 -.495a2 2 0 0 1 2.828 0l7.071 7.07a2 2 0 0 1 0 2.83l-7.07 7.07a2 2 0 0 1 -2.83 0l-7.07 -7.07a2 2 0 0 1 0 -2.83l3.535 -3.535h-3.988"> - - - - Stop - - + + Redeploy + @endif + + @endcan + @if (data_get($preview, 'status') !== 'exited') + @can('deploy', $application) + + + + + + + + + + Stop + + + @endcan @endif - + @can('delete', $application) + + @endcan
    @endforeach diff --git a/resources/views/livewire/project/application/rollback.blade.php b/resources/views/livewire/project/application/rollback.blade.php index cf4467534..1844316f9 100644 --- a/resources/views/livewire/project/application/rollback.blade.php +++ b/resources/views/livewire/project/application/rollback.blade.php @@ -1,7 +1,9 @@

    Rollback

    - Reload Available Images + @can('view', $application) + Reload Available Images + @endcan
    You can easily rollback to a previously built (local) images quickly.
    @@ -26,16 +28,18 @@
    {{ $date }}
    - @if (data_get($image, 'is_current')) - - Rollback - - @else - - Rollback - - @endif + @can('deploy', $application) + @if (data_get($image, 'is_current')) + + Rollback + + @else + + Rollback + + @endif + @endcan
    diff --git a/resources/views/livewire/project/application/source.blade.php b/resources/views/livewire/project/application/source.blade.php index 85382055e..9e746fadb 100644 --- a/resources/views/livewire/project/application/source.blade.php +++ b/resources/views/livewire/project/application/source.blade.php @@ -2,7 +2,9 @@
    @endif
    - - + +
    - +
    @@ -46,45 +48,49 @@ class="font-bold text-warning">{{ data_get($application, 'source.name', 'No sour class="dark:text-warning">{{ $privateKeyName }}
    -

    Select another Private Key

    -
    - @foreach ($privateKeys as $key) - {{ $key->name }} - - @endforeach -
    - @else -
    -

    Change Git Source

    -
    - @forelse ($sources as $source) -
    - - -
    -
    - {{ $source->name }} - @if ($application->source_id === $source->id) - (current) - @endif -
    -
    - {{ $source->organization ?? 'Personal Account' }} -
    -
    -
    -
    -
    - @empty -
    No other sources found
    - @endforelse + @can('update', $application) +

    Select another Private Key

    +
    + @foreach ($privateKeys as $key) + {{ $key->name }} + + @endforeach
    -
    + @endcan + @else + @can('update', $application) +
    +

    Change Git Source

    +
    + @forelse ($sources as $source) +
    + + +
    +
    + {{ $source->name }} + @if ($application->source_id === $source->id) + (current) + @endif +
    +
    + {{ $source->organization ?? 'Personal Account' }} +
    +
    +
    +
    +
    + @empty +
    No other sources found
    + @endforelse +
    +
    + @endcan @endif
    diff --git a/resources/views/livewire/project/application/swarm.blade.php b/resources/views/livewire/project/application/swarm.blade.php index 1822f948b..a7da02627 100644 --- a/resources/views/livewire/project/application/swarm.blade.php +++ b/resources/views/livewire/project/application/swarm.blade.php @@ -15,14 +15,14 @@
    - + + id="isSwarmOnlyWorkerNodes" label="Only Start on Worker nodes" canGate="update" :canResource="$application" />
    + - 'node.role == worker'" canGate="update" :canResource="$application" />
    diff --git a/resources/views/livewire/project/database/backup/index.blade.php b/resources/views/livewire/project/database/backup/index.blade.php index 91f0676aa..05a0ce04e 100644 --- a/resources/views/livewire/project/database/backup/index.blade.php +++ b/resources/views/livewire/project/database/backup/index.blade.php @@ -8,9 +8,11 @@

    Scheduled Backups

    - - - + @can('update', $database) + + + + @endcan
    diff --git a/resources/views/livewire/project/database/clickhouse/general.blade.php b/resources/views/livewire/project/database/clickhouse/general.blade.php index e91663f35..9017f7c09 100644 --- a/resources/views/livewire/project/database/clickhouse/general.blade.php +++ b/resources/views/livewire/project/database/clickhouse/general.blade.php @@ -2,54 +2,56 @@

    General

    - + Save
    - - - + +
    @if ($database->started_at)
    + readonly helper="You can only change this in the database." canGate="update" :canResource="$database" /> + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    @else
    Please verify these values. You can only modify them before the initial start. After that, you need to modify it in the database.
    - - + +
    @endif + id="customDockerRunOptions" label="Custom Docker Options" canGate="update" :canResource="$database" />

    Network

    + helper="A comma separated list of ports you would like to map to the host system.
    Example3000:5432,3002:5433" + canGate="update" :canResource="$database" />
    + type="password" readonly wire:model="dbUrl" canGate="update" :canResource="$database" /> @if ($dbUrlPublic) + type="password" readonly wire:model="dbUrlPublic" canGate="update" :canResource="$database" /> @else + readonly value="Starting the database will generate this." canGate="update" :canResource="$database" /> @endif
    @@ -71,14 +73,17 @@ @endif
    - +
    - +

    Advanced

    + instantSave="instantSaveAdvanced" id="isLogDrainEnabled" label="Drain Logs" canGate="update" + :canResource="$database" />
    diff --git a/resources/views/livewire/project/database/configuration.blade.php b/resources/views/livewire/project/database/configuration.blade.php index 5e1600461..a57f0fac2 100644 --- a/resources/views/livewire/project/database/configuration.blade.php +++ b/resources/views/livewire/project/database/configuration.blade.php @@ -17,9 +17,11 @@ Persistent Storage - Import - Backups + @can('update', $database) + Import + Backups + @endcan Webhooks

    General

    - + Save
    - - - + + +
    + id="customDockerRunOptions" label="Custom Docker Options" canGate="update" :canResource="$database" /> @if ($database->started_at)
    + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    @else
    Please verify these values. You can only modify them before the initial start. After that, you need to modify it in the database.
    - +
    @endif

    Network

    + helper="A comma separated list of ports you would like to map to the host system.
    Example3000:5432,3002:5433" + canGate="update" :canResource="$database" />
    + type="password" readonly wire:model="dbUrl" canGate="update" :canResource="$database" /> @if ($dbUrlPublic) + type="password" readonly wire:model="dbUrlPublic" canGate="update" :canResource="$database" /> @else + readonly value="Starting the database will generate this." canGate="update" :canResource="$database" /> @endif
    @@ -79,11 +81,12 @@
    @if (str($database->status)->contains('exited')) + instantSave="instantSaveSSL" canGate="update" :canResource="$database" /> @else + helper="Database should be stopped to change this settings." canGate="update" + :canResource="$database" /> @endif
    @@ -107,14 +110,17 @@ @endif
    - +
    - +

    Advanced

    + instantSave="instantSaveAdvanced" id="isLogDrainEnabled" label="Drain Logs" canGate="update" + :canResource="$database" />
    diff --git a/resources/views/livewire/project/database/heading.blade.php b/resources/views/livewire/project/database/heading.blade.php index 3df3b0e53..b09adcc4e 100644 --- a/resources/views/livewire/project/database/heading.blade.php +++ b/resources/views/livewire/project/database/heading.blade.php @@ -18,10 +18,12 @@ class="flex overflow-x-scroll shrink-0 gap-6 items-center whitespace-nowrap sm:o href="{{ route('project.database.logs', $parameters) }}"> Logs - - Terminal - + @can('canAccessTerminal') + + Terminal + + @endcan @if ( $database->getMorphClass() === 'App\Models\StandalonePostgresql' || $database->getMorphClass() === 'App\Models\StandaloneMongodb' || diff --git a/resources/views/livewire/project/database/import.blade.php b/resources/views/livewire/project/database/import.blade.php index d7baf602f..c6501e031 100644 --- a/resources/views/livewire/project/database/import.blade.php +++ b/resources/views/livewire/project/database/import.blade.php @@ -108,8 +108,8 @@
    Location: /
    Restore Backup
    -
    - +
    +
    @else
    Database must be running to restore a backup.
    diff --git a/resources/views/livewire/project/database/keydb/general.blade.php b/resources/views/livewire/project/database/keydb/general.blade.php index 065303f75..a4b0eb471 100644 --- a/resources/views/livewire/project/database/keydb/general.blade.php +++ b/resources/views/livewire/project/database/keydb/general.blade.php @@ -2,51 +2,53 @@

    General

    - + Save
    - - - + +
    @if ($database->started_at)
    + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    @else
    Please verify these values. You can only modify them before the initial start. After that, you need to modify it in the database.
    - +
    @endif + id="customDockerRunOptions" label="Custom Docker Options" canGate="update" :canResource="$database" />

    Network

    + helper="A comma separated list of ports you would like to map to the host system.
    Example3000:5432,3002:5433" + canGate="update" :canResource="$database" />
    + type="password" readonly wire:model="dbUrl" canGate="update" :canResource="$database" /> @if ($dbUrlPublic) + type="password" readonly wire:model="dbUrlPublic" canGate="update" :canResource="$database" /> @else + readonly value="Starting the database will generate this." canGate="update" :canResource="$database" /> @endif
    @@ -79,11 +81,12 @@
    @if (str($database->status)->contains('exited')) + instantSave="instantSaveSSL" canGate="update" :canResource="$database" /> @else + helper="Database should be stopped to change this settings." canGate="update" + :canResource="$database" /> @endif
    @@ -107,17 +110,20 @@ @endif
    - +
    - + + label="Custom KeyDB Configuration" rows="10" id="keydbConf" canGate="update" :canResource="$database" />

    Advanced

    + instantSave="instantSaveAdvanced" id="isLogDrainEnabled" label="Drain Logs" canGate="update" + :canResource="$database" />
    diff --git a/resources/views/livewire/project/database/mariadb/general.blade.php b/resources/views/livewire/project/database/mariadb/general.blade.php index 6186e4a82..fdc659408 100644 --- a/resources/views/livewire/project/database/mariadb/general.blade.php +++ b/resources/views/livewire/project/database/mariadb/general.blade.php @@ -2,14 +2,14 @@

    General

    - + Save
    - - - + +
    If you change the values in the database, please sync it here, otherwise @@ -18,11 +18,14 @@ @if ($database->started_at)
    + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." + canGate="update" :canResource="$database" /> + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." + canGate="update" :canResource="$database" /> + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." + canGate="update" :canResource="$database" />
    + helper="You can only change this in the database." canGate="update" :canResource="$database" /> + helper="You can only change this in the database." canGate="update" :canResource="$database" /> + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    @endif
    + id="database.custom_docker_run_options" label="Custom Docker Options" canGate="update" + :canResource="$database" />

    Network

    + helper="A comma separated list of ports you would like to map to the host system.
    Example3000:5432,3002:5433" + canGate="update" :canResource="$database" />
    + type="password" readonly wire:model="db_url" canGate="update" :canResource="$database" /> @if ($db_url_public) + type="password" readonly wire:model="db_url_public" canGate="update" :canResource="$database" /> @endif
    @@ -98,11 +103,13 @@
    @if (str($database->status)->contains('exited')) + wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" canGate="update" + :canResource="$database" /> @else + helper="Database should be stopped to change this settings." canGate="update" + :canResource="$database" /> @endif
    @@ -127,16 +134,19 @@ @endif - + + id="database.public_port" label="Public Port" canGate="update" :canResource="$database" /> - +

    Advanced

    + instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" + canGate="update" :canResource="$database" />
    diff --git a/resources/views/livewire/project/database/mongodb/general.blade.php b/resources/views/livewire/project/database/mongodb/general.blade.php index c92143336..2892e721e 100644 --- a/resources/views/livewire/project/database/mongodb/general.blade.php +++ b/resources/views/livewire/project/database/mongodb/general.blade.php @@ -2,14 +2,14 @@

    General

    - + Save
    - - - + +
    If you change the values in the database, please sync it here, otherwise @@ -19,40 +19,44 @@
    + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." + canGate="update" :canResource="$database" /> + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." + canGate="update" :canResource="$database" /> + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    @else
    - + placeholder="If empty: postgres" canGate="update" :canResource="$database" /> + + placeholder="If empty, it will be the same as Username." canGate="update" :canResource="$database" />
    @endif + id="database.custom_docker_run_options" label="Custom Docker Options" canGate="update" :canResource="$database" />

    Network

    + helper="A comma separated list of ports you would like to map to the host system.
    Example3000:5432,3002:5433" + canGate="update" :canResource="$database" />
    + type="password" readonly wire:model="db_url" canGate="update" :canResource="$database" /> @if ($db_url_public) + type="password" readonly wire:model="db_url_public" canGate="update" :canResource="$database" /> @endif
    @@ -88,11 +92,13 @@
    @if (str($database->status)->contains('exited')) + wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" canGate="update" + :canResource="$database" /> @else + helper="Database should be stopped to change this settings." canGate="update" + :canResource="$database" /> @endif
    @if ($database->enable_ssl) @@ -100,7 +106,8 @@ @if (str($database->status)->contains('exited')) + helper="Choose the SSL verification mode for MongoDB connections" canGate="update" + :canResource="$database"> @@ -109,7 +116,8 @@ @else + disabled helper="Database should be stopped to change this settings." canGate="update" + :canResource="$database"> @@ -140,16 +148,20 @@ @endif
    - + + id="database.public_port" label="Public Port" canGate="update" :canResource="$database" /> - +

    Advanced

    + instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" + canGate="update" :canResource="$database" />
    + diff --git a/resources/views/livewire/project/database/mysql/general.blade.php b/resources/views/livewire/project/database/mysql/general.blade.php index c9090a4db..c04119f9f 100644 --- a/resources/views/livewire/project/database/mysql/general.blade.php +++ b/resources/views/livewire/project/database/mysql/general.blade.php @@ -7,10 +7,10 @@
    - - + + + helper="For all available images, check here:

    https://hub.docker.com/_/mysql" canGate="update" :canResource="$database" />
    If you change the values in the database, please sync it here, otherwise automations (like backups) won't work. @@ -18,43 +18,43 @@ @if ($database->started_at)
    + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." canGate="update" :canResource="$database" /> + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." canGate="update" :canResource="$database" /> + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." canGate="update" :canResource="$database" />
    + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    @else
    + helper="You can only change this in the database." canGate="update" :canResource="$database" /> + helper="You can only change this in the database." canGate="update" :canResource="$database" /> + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    @endif
    + id="database.custom_docker_run_options" label="Custom Docker Options" canGate="update" :canResource="$database" />

    Network

    + helper="A comma separated list of ports you would like to map to the host system.
    Example3000:5432,3002:5433" canGate="update" :canResource="$database" />
    @if (str($database->status)->contains('exited')) + wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" canGate="update" :canResource="$database" /> @else status)->contains('exited')) + helper="Choose the SSL verification mode for MySQL connections" canGate="update" :canResource="$database"> @@ -151,16 +151,16 @@ @endif
    - +
    + id="database.public_port" label="Public Port" canGate="update" :canResource="$database" /> - +

    Advanced

    + instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" canGate="update" :canResource="$database" />
    diff --git a/resources/views/livewire/project/database/postgresql/general.blade.php b/resources/views/livewire/project/database/postgresql/general.blade.php index 50575f6c7..d93170edf 100644 --- a/resources/views/livewire/project/database/postgresql/general.blade.php +++ b/resources/views/livewire/project/database/postgresql/general.blade.php @@ -16,14 +16,14 @@

    General

    - + Save
    - - - + +
    If you change the values in the database, please sync it here, otherwise @@ -32,8 +32,10 @@ @if ($database->started_at)
    @else
    - - + + + placeholder="If empty, it will be the same as Username." canGate="update" :canResource="$database" />
    @endif
    - - + +
    + id="database.custom_docker_run_options" label="Custom Docker Options" canGate="update" :canResource="$database" />

    Network

    + helper="A comma separated list of ports you would like to map to the host system.
    Example3000:5432,3002:5433" + canGate="update" :canResource="$database" />
    + ]" submitAction="regenerateSslCertificate" :confirmWithText="false" + :confirmWithPassword="false" /> @endif
    @if ($database->enable_ssl && $certificateValidUntil) @@ -102,7 +108,8 @@
    @if ($database->isExited()) + wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" canGate="update" + :canResource="$database" /> @else isExited()) + helper="Choose the SSL verification mode for PostgreSQL connections" canGate="update" + :canResource="$database"> @@ -153,15 +161,16 @@ @endif
    - +
    + id="database.public_port" label="Public Port" canGate="update" :canResource="$database" />
    + id="database.postgres_conf" canGate="update" :canResource="$database" />
    @@ -169,23 +178,27 @@

    Advanced

    + instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" + canGate="update" :canResource="$database" />
    +

    Initialization scripts

    - -
    - - - - Save - - -
    + @can('update', $database) + +
    + + + + Save + + +
    + @endcan
    @forelse(data_get($database,'init_scripts', []) as $script) diff --git a/resources/views/livewire/project/database/redis/general.blade.php b/resources/views/livewire/project/database/redis/general.blade.php index b4876f325..66e913be0 100644 --- a/resources/views/livewire/project/database/redis/general.blade.php +++ b/resources/views/livewire/project/database/redis/general.blade.php @@ -2,14 +2,14 @@

    General

    - + Save
    - - - + +
    @@ -21,10 +21,10 @@
    @if (version_compare($redis_version, '6.0', '>=')) + helper="You can only change this in the database." canGate="update" :canResource="$database" /> @endif + helper="You can only change this in the database." canGate="update" :canResource="$database" />
    @else
    You can only change the username and password in the database after @@ -37,7 +37,7 @@ If you change the Redis Username in the database, please sync it here, otherwise automations (like backups) won't work.

    Note: If the environment variable REDIS_USERNAME is set as a shared variable (environment, project, or team-based), this input field will become read-only." - :disabled="$this->isSharedVariable('REDIS_USERNAME')" /> + :disabled="$this->isSharedVariable('REDIS_USERNAME')" canGate="update" :canResource="$database" /> @endif + :disabled="$this->isSharedVariable('REDIS_PASSWORD')" canGate="update" :canResource="$database" />
    @endif
    + id="database.custom_docker_run_options" label="Custom Docker Options" canGate="update" :canResource="$database" />

    Network

    + helper="A comma separated list of ports you would like to map to the host system.
    Example3000:5432,3002:5433" + canGate="update" :canResource="$database" />
    + type="password" readonly wire:model="db_url" canGate="update" :canResource="$database" /> @if ($db_url_public) + type="password" readonly wire:model="db_url_public" canGate="update" :canResource="$database" /> @endif
    @@ -98,11 +99,13 @@
    @if (str($database->status)->contains('exited')) + wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" canGate="update" + :canResource="$database" /> @else + helper="Database should be stopped to change this settings." canGate="update" + :canResource="$database" /> @endif
    @@ -126,26 +129,28 @@ @endif
    - +
    + id="database.public_port" label="Public Port" canGate="update" :canResource="$database" />
    -
    ⚠️ Important: Coolify automatically applies the requirepass directive using the password shown in the Password field above. If you override requirepass in your custom configuration, make sure it matches the password field to avoid authentication issues.

    🔗 Tip: View the full Redis default configuration to see what options are available." - label="Custom Redis Configuration" rows="10" id="database.redis_conf" /> + label="Custom Redis Configuration" rows="10" id="database.redis_conf" canGate="update" + :canResource="$database" />

    Advanced

    + instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" + canGate="update" :canResource="$database" />
    diff --git a/resources/views/livewire/project/database/scheduled-backups.blade.php b/resources/views/livewire/project/database/scheduled-backups.blade.php index 7872e436a..c0b8647b2 100644 --- a/resources/views/livewire/project/database/scheduled-backups.blade.php +++ b/resources/views/livewire/project/database/scheduled-backups.blade.php @@ -62,7 +62,7 @@ @else + class="px-3 py-1 rounded-md text-xs font-medium tracking-wide shadow-xs bg-gray-100 text-gray-800 dark:bg-neutral-800 dark:text-gray-200"> No executions yet @endif @@ -146,7 +146,7 @@ class="px-3 py-1 rounded-md text-xs font-medium tracking-wide shadow-xs bg-gray- @else + class="px-3 py-1 rounded-md text-xs font-medium tracking-wide shadow-xs bg-gray-100 text-gray-800 dark:bg-neutral-800 dark:text-gray-200"> No executions yet @endif diff --git a/resources/views/livewire/project/environment-edit.blade.php b/resources/views/livewire/project/environment-edit.blade.php index 7062d7e0a..078d93456 100644 --- a/resources/views/livewire/project/environment-edit.blade.php +++ b/resources/views/livewire/project/environment-edit.blade.php @@ -5,16 +5,7 @@

    Environment: {{ data_get_str($environment, 'name')->limit(15) }}

    - @can('update', $environment) - - Save - - @else - - Save - - @endcan + Save @can('delete', $environment) @endcan diff --git a/resources/views/livewire/project/index.blade.php b/resources/views/livewire/project/index.blade.php index 46ded4424..89da9117e 100644 --- a/resources/views/livewire/project/index.blade.php +++ b/resources/views/livewire/project/index.blade.php @@ -4,9 +4,11 @@

    Projects

    - - - + @can('createAnyResource') + + + + @endcan
    All your projects are here.
    @@ -24,8 +26,8 @@
    - @@ -149,13 +153,15 @@ class="w-4 h-4 dark:text-warning text-coollabs" Settings @if (str($database->status)->contains('running')) - + @can('update', $service) + + @endcan @endif diff --git a/resources/views/livewire/project/service/database.blade.php b/resources/views/livewire/project/service/database.blade.php index b066a22b1..117ad44c5 100644 --- a/resources/views/livewire/project/service/database.blade.php +++ b/resources/views/livewire/project/service/database.blade.php @@ -6,29 +6,33 @@ @else

    {{ Str::headline($database->name) }}

    @endif - Save - - + Save + @can('update', $database) + + @endcan + @can('delete', $database) + + @endcan
    - - - + +
    - - +
    @if ($db_url_public)

    Advanced

    - -
    diff --git a/resources/views/livewire/project/service/edit-domain.blade.php b/resources/views/livewire/project/service/edit-domain.blade.php index 5528834eb..1b4fa48fd 100644 --- a/resources/views/livewire/project/service/edit-domain.blade.php +++ b/resources/views/livewire/project/service/edit-domain.blade.php @@ -1,7 +1,7 @@
    Note: If a service has a defined port, do not delete it.
    If you want to use your custom domain, you can add it with a port.
    - - Save + Save
    diff --git a/resources/views/livewire/project/service/file-storage.blade.php b/resources/views/livewire/project/service/file-storage.blade.php index 0637c9e86..aa0ce66a3 100644 --- a/resources/views/livewire/project/service/file-storage.blade.php +++ b/resources/views/livewire/project/service/file-storage.blade.php @@ -6,54 +6,68 @@
    -
    - @if ($fileStorage->is_directory) - - - @else - @if (!$fileStorage->is_binary) - + @if ($fileStorage->is_directory) + + shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to file" /> + + @else + @if (!$fileStorage->is_binary) + + @endif + Load from server + @endif - Load from server - - @endif -
    + + @endcan @if (!$fileStorage->is_directory) - @if (data_get($resource, 'settings.is_preserve_repository_enabled')) -
    - -
    - @endif - - @if (!$fileStorage->is_based_on_git && !$fileStorage->is_binary) - Save - @endif + @can('update', $resource) + @if (data_get($resource, 'settings.is_preserve_repository_enabled')) +
    + +
    + @endif + + @if (!$fileStorage->is_based_on_git && !$fileStorage->is_binary) + Save + @endif + @else + @if (data_get($resource, 'settings.is_preserve_repository_enabled')) +
    + +
    + @endif + + @endcan @endif
    diff --git a/resources/views/livewire/project/service/heading.blade.php b/resources/views/livewire/project/service/heading.blade.php index 8afade4b1..73635d93a 100644 --- a/resources/views/livewire/project/service/heading.blade.php +++ b/resources/views/livewire/project/service/heading.blade.php @@ -18,10 +18,12 @@ href="{{ route('project.service.logs', $parameters) }}"> - - - + @can('canAccessTerminal') + + + + @endcan @if ($service->isDeployable) diff --git a/resources/views/livewire/project/service/index.blade.php b/resources/views/livewire/project/service/index.blade.php index d6afe90e3..9a782fca6 100644 --- a/resources/views/livewire/project/service/index.blade.php +++ b/resources/views/livewire/project/service/index.blade.php @@ -38,9 +38,11 @@ class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-activ

    Scheduled Backups

    @if (filled($serviceDatabase->custom_type) || !$serviceDatabase->is_migrated) - - - + @can('update', $serviceDatabase) + + + + @endcan @endif
    diff --git a/resources/views/livewire/project/service/service-application-view.blade.php b/resources/views/livewire/project/service/service-application-view.blade.php index 4685741a7..deaf1429c 100644 --- a/resources/views/livewire/project/service/service-application-view.blade.php +++ b/resources/views/livewire/project/service/service-application-view.blade.php @@ -6,35 +6,41 @@ @else

    {{ Str::headline($application->name) }}

    @endif - Save - - + Save + @can('update', $application) + + @endcan + @can('delete', $application) + + @endcan
    - - +
    @if (!$application->serviceType()?->contains(str($application->image)->before(':'))) @if ($application->required_fqdn) - @else - @endif @endif -
    @@ -42,18 +48,22 @@

    Advanced

    @if (str($application->image)->contains('pocketbase')) - @else - @endif - - -
    diff --git a/resources/views/livewire/project/service/stack-form.blade.php b/resources/views/livewire/project/service/stack-form.blade.php index 7583cabaf..fff6524ce 100644 --- a/resources/views/livewire/project/service/stack-form.blade.php +++ b/resources/views/livewire/project/service/stack-form.blade.php @@ -5,19 +5,21 @@ @if (isDev())
    {{ $service->compose_parsing_version }}
    @endif - Save - - - + Save + @can('update', $service) + + + + @endcan
    Configuration
    - - + +
    -
    @if ($fields->count() > 0) @@ -34,7 +36,7 @@ class="font-bold">{{ data_get($field, 'serviceName') }}{{ data_get($field @endif - @endforeach diff --git a/resources/views/livewire/project/service/storage.blade.php b/resources/views/livewire/project/service/storage.blade.php index 0988788e9..41d48f386 100644 --- a/resources/views/livewire/project/service/storage.blade.php +++ b/resources/views/livewire/project/service/storage.blade.php @@ -16,9 +16,11 @@ volume name, example: -pr-1" /> @if ($resource?->build_pack !== 'dockercompose') - - - + @can('update', $resource) + + + + @endcan @endif
    Persistent storage to preserve data between deployments.
    diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index a71f88637..c75407179 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -2,20 +2,28 @@

    Environment Variables

    -
    - - - -
    - {{ $view === 'normal' ? 'Developer view' : 'Normal view' }} + @can('manageEnvironment', $resource) +
    + + + +
    + {{ $view === 'normal' ? 'Developer view' : 'Normal view' }} + @endcan
    Environment variables (secrets) for this resource.
    @if ($resourceClass === 'App\Models\Application' && data_get($resource, 'build_pack') !== 'dockercompose')
    - + @can('manageEnvironment', $resource) + + @else + + @endcan
    @endif @if ($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') @@ -62,16 +70,27 @@ @endif @else
    - + @can('manageEnvironment', $resource) + - @if ($showPreview) - - @endif + @if ($showPreview) + + @endif - Save All Environment Variables + Save All Environment Variables + @else + + + @if ($showPreview) + + @endif + @endcan
    @endif
    diff --git a/resources/views/livewire/project/shared/environment-variable/show.blade.php b/resources/views/livewire/project/shared/environment-variable/show.blade.php index 5a197dc7a..63148b74c 100644 --- a/resources/views/livewire/project/shared/environment-variable/show.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/show.blade.php @@ -13,101 +13,151 @@ - + @can('delete', $this->env) + + @endcan @else - @if ($isDisabled) -
    + @can('update', $this->env) + @if ($isDisabled) +
    + + + @if ($is_shared) + + @endif +
    + @else +
    + @if ($is_multiline) + + + @else + + + @endif + @if ($is_shared) + + @endif +
    + @endif + @else +
    @if ($is_shared) @endif
    - @else -
    - @if ($is_multiline) - - - @else - - - @endif - @if ($is_shared) - - @endif -
    - @endif -
    - @if (!$is_redis_credential) - @if ($type === 'service') - - - - @else - @if ($is_shared) + @endcan + @can('manageEnvironment', $this->resource) +
    + @if (!$is_redis_credential) + @if ($type === 'service') + @else - @if ($isSharedVariable) - - @else + @if ($is_shared) - - @if ($is_multiline === false) - + + @else + @if ($isSharedVariable) + + @else + + + @if ($is_multiline === false) + + @endif @endif @endif @endif @endif - @endif -
    - @if ($isDisabled) - - Update - - - Lock - - - @else - - Update - - - Lock - - - @endif -
    +
    + @if ($isDisabled) + + Update + + + Lock + + + @else + + Update + + + Lock + + + @endif +
    + @else +
    + @if (!$is_redis_credential) + @if ($type === 'service') + + + + @else + @if ($is_shared) + + + @else + @if ($isSharedVariable) + + @else + + + @if ($is_multiline === false) + + @endif + @endif + @endif + @endif + @endif +
    +
    + @endcan @endif diff --git a/resources/views/livewire/project/shared/health-checks.blade.php b/resources/views/livewire/project/shared/health-checks.blade.php index cb34f61ed..fb9f7f3bc 100644 --- a/resources/views/livewire/project/shared/health-checks.blade.php +++ b/resources/views/livewire/project/shared/health-checks.blade.php @@ -1,7 +1,7 @@

    Healthchecks

    - Save + Save
    Define how your resource's health should be checked.
    @@ -10,29 +10,29 @@
    @endif
    - +
    - - - - + + + - +
    - - +
    - - - - +
    - + \ No newline at end of file diff --git a/resources/views/livewire/project/shared/resource-limits.blade.php b/resources/views/livewire/project/shared/resource-limits.blade.php index 6c84335de..2aa2fd0af 100644 --- a/resources/views/livewire/project/shared/resource-limits.blade.php +++ b/resources/views/livewire/project/shared/resource-limits.blade.php @@ -2,40 +2,39 @@

    Resource Limits

    - Save + Save
    Limit your container resources by CPU & memory.

    Limit CPUs

    - - -

    Limit Memory

    - -
    - - -
    diff --git a/resources/views/livewire/project/shared/resource-operations.blade.php b/resources/views/livewire/project/shared/resource-operations.blade.php index f23c6eb4e..aa8f536ce 100644 --- a/resources/views/livewire/project/shared/resource-operations.blade.php +++ b/resources/views/livewire/project/shared/resource-operations.blade.php @@ -1,30 +1,41 @@

    Resource Operations

    You can easily make different kind of operations on this resource.
    -

    Clone

    +

    Clone

    To another project / environment on a different / same server.
    - @foreach ($servers->sortBy('id') as $server) -
    Server: {{ $server->name }}
    - @foreach ($server->destinations() as $destination) - - -
    -
    -
    Network
    -
    {{ $destination->name }}
    + @can('update', $resource) + @foreach ($servers->sortBy('id') as $server) +
    Server: {{ $server->name }}
    + @foreach ($server->destinations() as $destination) + + +
    +
    +
    Network
    +
    {{ $destination->name }}
    +
    -
    - - + + + @endforeach @endforeach - @endforeach + @else +
    +
    + Access Restricted: You don't have permission to clone resources. Contact your team + administrator to request access. +
    +
    + @endcan

    Move

    @@ -36,28 +47,38 @@ class="font-bold dark:text-warning">{{ $resource->environment->project->name }} {{ $resource->environment->name }} environment.
    - @forelse ($projects as $project) -
    Project: {{ $project->name }}
    + @can('update', $resource) + @forelse ($projects as $project) +
    Project: {{ $project->name }}
    - @foreach ($project->environments as $environment) - - -
    -
    -
    Environment
    -
    {{ $environment->name }}
    + @foreach ($project->environments as $environment) + + +
    +
    +
    Environment
    +
    {{ $environment->name }}
    +
    -
    - - - @endforeach - @empty -
    No projects found to move to
    - @endforelse + + + @endforeach + @empty +
    No projects found to move to
    + @endforelse + @else +
    +
    + Access Restricted: You don't have permission to move resources between projects or + environments. Contact your team administrator to request access. +
    +
    + @endcan
    diff --git a/resources/views/livewire/project/shared/scheduled-task/all.blade.php b/resources/views/livewire/project/shared/scheduled-task/all.blade.php index 8a2ec4d7a..fb6985094 100644 --- a/resources/views/livewire/project/shared/scheduled-task/all.blade.php +++ b/resources/views/livewire/project/shared/scheduled-task/all.blade.php @@ -1,13 +1,15 @@

    Scheduled Tasks

    - - @if ($resource->type() == 'application') - - @elseif ($resource->type() == 'service') - - @endif - + @can('update', $resource) + + @if ($resource->type() == 'application') + + @elseif ($resource->type() == 'service') + + @endif + + @endcan
    @forelse($resource->scheduled_tasks as $task) diff --git a/resources/views/livewire/project/shared/storages/all.blade.php b/resources/views/livewire/project/shared/storages/all.blade.php index e357b4f94..f7bd7bdce 100644 --- a/resources/views/livewire/project/shared/storages/all.blade.php +++ b/resources/views/livewire/project/shared/storages/all.blade.php @@ -3,10 +3,10 @@ @foreach ($resource->persistentStorages as $storage) @if ($resource->type() === 'service') + :resource="$resource" :isFirst="$loop->first" isReadOnly='true' isService='true' /> @else @endif @endforeach diff --git a/resources/views/livewire/project/shared/storages/show.blade.php b/resources/views/livewire/project/shared/storages/show.blade.php index 4ad5636ec..569df0c4b 100644 --- a/resources/views/livewire/project/shared/storages/show.blade.php +++ b/resources/views/livewire/project/shared/storages/show.blade.php @@ -9,7 +9,7 @@ @else - @endif @if ($isService || $startedAt) @@ -19,13 +19,11 @@ @else - - - Update - @endif
    @else @@ -36,32 +34,50 @@
    @endif @else - @if ($isFirst) -
    - - - + @can('update', $resource) + @if ($isFirst) +
    + + + +
    + @else +
    + + + +
    + @endif +
    + + Update + +
    @else -
    - - - -
    - @endif -
    - - Update - - -
    + @if ($isFirst) +
    + + + +
    + @else +
    + + + +
    + @endif + @endcan @endif
    diff --git a/resources/views/livewire/project/shared/tags.blade.php b/resources/views/livewire/project/shared/tags.blade.php index 4ceb475a6..2c75deab9 100644 --- a/resources/views/livewire/project/shared/tags.blade.php +++ b/resources/views/livewire/project/shared/tags.blade.php @@ -1,37 +1,50 @@

    Tags

    -
    -
    - + @can('update', $resource) + +
    + +
    + Add + + @else +
    +
    + Access Restricted: You don't have permission to manage tags. Contact your team + administrator to request access. +
    - Add - + @endcan @if (data_get($this->resource, 'tags') && count(data_get($this->resource, 'tags')) > 0)

    Assigned Tags

    @foreach (data_get($this->resource, 'tags') as $tagId => $tag)
    {{ $tag->name }} - - - - + @can('update', $resource) + + + + + @endcan
    @endforeach
    @endif - @if (count($filteredTags) > 0) -

    Existing Tags

    -
    Click to add quickly
    -
    - @foreach ($filteredTags as $tag) - - {{ $tag->name }} - @endforeach -
    - @endif + @can('update', $resource) + @if (count($filteredTags) > 0) +

    Existing Tags

    +
    Click to add quickly
    +
    + @foreach ($filteredTags as $tag) + + {{ $tag->name }} + @endforeach +
    + @endif + @endcan
    diff --git a/resources/views/livewire/project/shared/webhooks.blade.php b/resources/views/livewire/project/shared/webhooks.blade.php index 64a2ece51..f3403a402 100644 --- a/resources/views/livewire/project/shared/webhooks.blade.php +++ b/resources/views/livewire/project/shared/webhooks.blade.php @@ -17,10 +17,15 @@
    - - + @can('update', $resource) + + @else + + @endcan
    Webhook Configuration on GitHub @@ -29,23 +34,43 @@
    - + @can('update', $resource) + + @else + + @endcan
    - + @can('update', $resource) + + @else + + @endcan
    - + @can('update', $resource) + + @else + + @endcan
    - Save + @can('update', $resource) + Save + @endcan @else You are using an official Git App. You do not need manual webhooks. diff --git a/resources/views/livewire/project/show.blade.php b/resources/views/livewire/project/show.blade.php index da653398b..3d034b8f3 100644 --- a/resources/views/livewire/project/show.blade.php +++ b/resources/views/livewire/project/show.blade.php @@ -4,21 +4,16 @@

    Environments

    - -
    - - @can('update', $project) + @can('update', $project) + + + Save - @else - - Save - - @endcan - - + +
    + @endcan @can('delete', $project) @endcan @@ -34,12 +29,14 @@
    {{ $environment->description }}
    - + @can('update', $project) + + @endcan
    @empty diff --git a/resources/views/livewire/security/api-tokens.blade.php b/resources/views/livewire/security/api-tokens.blade.php index eaf7a439d..bf6bcf76c 100644 --- a/resources/views/livewire/security/api-tokens.blade.php +++ b/resources/views/livewire/security/api-tokens.blade.php @@ -12,44 +12,58 @@
    Tokens are created with the current team as scope.

    New Token

    -
    -
    - - Create -
    -
    - Permissions - : -
    - @if ($permissions) - @foreach ($permissions as $permission) -
    {{ $permission }}
    - @endforeach + @can('create', App\Models\PersonalAccessToken::class) + +
    + + Create +
    +
    + Permissions + : +
    + @if ($permissions) + @foreach ($permissions as $permission) +
    {{ $permission }}
    + @endforeach + @endif +
    +
    + +

    Token Permissions

    +
    + @if ($canUseRootPermissions) + + @else + + @endif + + @if (!in_array('root', $permissions)) + @if ($canUseWritePermissions) + + @else + + @endif + + + + @endif
    -
    - -

    Token Permissions

    -
    - - @if (!in_array('root', $permissions)) - - - - + @if (in_array('root', $permissions)) +
    Root access, be careful!
    @endif -
    - @if (in_array('root', $permissions)) -
    Root access, be careful!
    - @endif - + + @endcan @if (session()->has('token'))
    Please copy this token now. For your security, it won't be shown again. @@ -72,15 +86,17 @@ class="flex flex-col gap-1 p-2 border dark:border-coolgray-200 hover:no-underlin @endif
    - + @if (auth()->id() === $token->tokenable_id) + + @endif
    @empty
    diff --git a/resources/views/livewire/security/private-key/index.blade.php b/resources/views/livewire/security/private-key/index.blade.php index f40b91f43..47cfc9b1e 100644 --- a/resources/views/livewire/security/private-key/index.blade.php +++ b/resources/views/livewire/security/private-key/index.blade.php @@ -2,11 +2,15 @@

    Private Keys

    - - - - + @can('create', App\Models\PrivateKey::class) + + + + @endcan + @can('create', App\Models\PrivateKey::class) + + @endcan
    @forelse ($privateKeys as $key) diff --git a/resources/views/livewire/security/private-key/show.blade.php b/resources/views/livewire/security/private-key/show.blade.php index c381b8f2a..8668cfd34 100644 --- a/resources/views/livewire/security/private-key/show.blade.php +++ b/resources/views/livewire/security/private-key/show.blade.php @@ -7,32 +7,34 @@

    Private Key

    - + Save @if (data_get($private_key, 'id') > 0) - + @can('delete', $private_key) + + @endcan @endif
    - - + +
    Public Key
    - +
    Private Key *
    @endif
    -
    - +
    diff --git a/resources/views/livewire/server/advanced.blade.php b/resources/views/livewire/server/advanced.blade.php index 98ab15534..308a9eed2 100644 --- a/resources/views/livewire/server/advanced.blade.php +++ b/resources/views/livewire/server/advanced.blade.php @@ -9,7 +9,7 @@

    Advanced

    - Save + Save
    Advanced configuration for your server.
    @@ -59,10 +59,10 @@ class="px-2 py-1 text-xs font-semibold text-red-800 bg-red-100 rounded dark:text
    - -
    @@ -71,9 +71,11 @@ class="px-2 py-1 text-xs font-semibold text-red-800 bg-red-100 rounded dark:text

    Builds

    - -
    diff --git a/resources/views/livewire/server/ca-certificate/show.blade.php b/resources/views/livewire/server/ca-certificate/show.blade.php index 66262614c..f11bd732e 100644 --- a/resources/views/livewire/server/ca-certificate/show.blade.php +++ b/resources/views/livewire/server/ca-certificate/show.blade.php @@ -8,28 +8,30 @@

    CA Certificate

    -
    - - - - -
    + @can('update', $server) +
    + + + + +
    + @endcan
    @@ -63,9 +65,11 @@ @endif
    - - {{ $showCertificate ? 'Hide' : 'Show' }} - + @can('view', $server) + + {{ $showCertificate ? 'Hide' : 'Show' }} + + @endcan
    @if ($showCertificate)