refactor(auth): enforce authorization checks across livewire components
Add authorization checks to multiple Livewire components to ensure users have proper permissions before performing sensitive operations. This includes: - Adding AuthorizesRequests trait to components handling deployments, backups, services, and configuration uploads - Enforcing 'deploy', 'update', and 'manageBackups' authorization checks - Adding instance admin check for system upgrade operations - Improving database queries with team ownership scope - Moving backup trigger from component to button with new backupNow() method
This commit is contained in:
parent
cebef8e258
commit
68f81df0bb
10 changed files with 105 additions and 30 deletions
|
|
@ -45,6 +45,9 @@ public function back()
|
|||
|
||||
public function submitSearch()
|
||||
{
|
||||
if (Auth::id() !== 0 && ! session('impersonating')) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
if ($this->search !== '') {
|
||||
$this->foundUsers = User::where(function ($query) {
|
||||
$query->where('name', 'like', "%{$this->search}%")
|
||||
|
|
@ -55,6 +58,9 @@ public function submitSearch()
|
|||
|
||||
public function getSubscribers()
|
||||
{
|
||||
if (Auth::id() !== 0 && ! session('impersonating')) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->inactiveSubscribers = Team::whereRelation('subscription', 'stripe_invoice_paid', false)->count();
|
||||
$this->activeSubscribers = Team::whereRelation('subscription', 'stripe_invoice_paid', true)->count();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ public function show_debug()
|
|||
public function force_start()
|
||||
{
|
||||
try {
|
||||
$this->authorize('deploy', $this->application);
|
||||
force_start_deployment($this->application_deployment_queue);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
|
@ -82,6 +83,11 @@ public function copyLogsToClipboard(): string
|
|||
|
||||
public function cancel()
|
||||
{
|
||||
try {
|
||||
$this->authorize('deploy', $this->application);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$deployment_uuid = $this->application_deployment_queue->deployment_uuid;
|
||||
$kill_command = "docker rm -f {$deployment_uuid}";
|
||||
$build_server_id = $this->application_deployment_queue->build_server_id ?? $this->application->destination->server_id;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@
|
|||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Swarm extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Application $application;
|
||||
|
||||
#[Validate('required')]
|
||||
|
|
@ -51,6 +54,7 @@ public function syncData(bool $toModel = false)
|
|||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->authorize('update', $this->application);
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Swarm settings updated.');
|
||||
} catch (\Throwable $e) {
|
||||
|
|
@ -61,6 +65,7 @@ public function instantSave()
|
|||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->authorize('update', $this->application);
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Swarm settings updated.');
|
||||
} catch (\Throwable $e) {
|
||||
|
|
|
|||
|
|
@ -201,6 +201,18 @@ public function delete($password)
|
|||
}
|
||||
}
|
||||
|
||||
public function backupNow()
|
||||
{
|
||||
try {
|
||||
$this->authorize('manageBackups', $this->backup->database);
|
||||
|
||||
\App\Jobs\DatabaseBackupJob::dispatch($this->backup);
|
||||
$this->dispatch('success', 'Backup queued. It will be available in a few minutes.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@
|
|||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Component;
|
||||
|
||||
class BackupExecutions extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public ?ScheduledDatabaseBackup $backup = null;
|
||||
|
||||
public $database;
|
||||
|
|
@ -44,29 +47,45 @@ public function getListeners()
|
|||
|
||||
public function cleanupFailed()
|
||||
{
|
||||
if ($this->backup) {
|
||||
$this->backup->executions()->where('status', 'failed')->delete();
|
||||
$this->refreshBackupExecutions();
|
||||
$this->dispatch('success', 'Failed backups cleaned up.');
|
||||
try {
|
||||
$this->authorize('manageBackups', $this->database);
|
||||
if ($this->backup) {
|
||||
$this->backup->executions()->where('status', 'failed')->delete();
|
||||
$this->refreshBackupExecutions();
|
||||
$this->dispatch('success', 'Failed backups cleaned up.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanupDeleted()
|
||||
{
|
||||
if ($this->backup) {
|
||||
$deletedCount = $this->backup->executions()->where('local_storage_deleted', true)->count();
|
||||
if ($deletedCount > 0) {
|
||||
$this->backup->executions()->where('local_storage_deleted', true)->delete();
|
||||
$this->refreshBackupExecutions();
|
||||
$this->dispatch('success', "Cleaned up {$deletedCount} backup entries deleted from local storage.");
|
||||
} else {
|
||||
$this->dispatch('info', 'No backup entries found that are deleted from local storage.');
|
||||
try {
|
||||
$this->authorize('manageBackups', $this->database);
|
||||
if ($this->backup) {
|
||||
$deletedCount = $this->backup->executions()->where('local_storage_deleted', true)->count();
|
||||
if ($deletedCount > 0) {
|
||||
$this->backup->executions()->where('local_storage_deleted', true)->delete();
|
||||
$this->refreshBackupExecutions();
|
||||
$this->dispatch('success', "Cleaned up {$deletedCount} backup entries deleted from local storage.");
|
||||
} else {
|
||||
$this->dispatch('info', 'No backup entries found that are deleted from local storage.');
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteBackup($executionId, $password)
|
||||
{
|
||||
try {
|
||||
$this->authorize('manageBackups', $this->database);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
if (! verifyPasswordConfirmation($password, $this)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@
|
|||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Service;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class EditCompose extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Service $service;
|
||||
|
||||
public $serviceId;
|
||||
|
|
@ -72,19 +75,29 @@ public function validateCompose()
|
|||
|
||||
public function saveEditedCompose()
|
||||
{
|
||||
$this->dispatch('info', 'Saving new docker compose...');
|
||||
$this->dispatch('saveCompose', $this->dockerComposeRaw);
|
||||
$this->dispatch('refreshStorages');
|
||||
try {
|
||||
$this->authorize('update', $this->service);
|
||||
$this->dispatch('info', 'Saving new docker compose...');
|
||||
$this->dispatch('saveCompose', $this->dockerComposeRaw);
|
||||
$this->dispatch('refreshStorages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
$this->validate([
|
||||
'isContainerLabelEscapeEnabled' => 'required',
|
||||
]);
|
||||
$this->syncData(true);
|
||||
$this->service->save(['is_container_label_escape_enabled' => $this->isContainerLabelEscapeEnabled]);
|
||||
$this->dispatch('success', 'Service updated successfully');
|
||||
try {
|
||||
$this->authorize('update', $this->service);
|
||||
$this->validate([
|
||||
'isContainerLabelEscapeEnabled' => 'required',
|
||||
]);
|
||||
$this->syncData(true);
|
||||
$this->service->save(['is_container_label_escape_enabled' => $this->isContainerLabelEscapeEnabled]);
|
||||
$this->dispatch('success', 'Service updated successfully');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@
|
|||
|
||||
use App\Models\Service;
|
||||
use App\Support\ValidationPatterns;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Component;
|
||||
|
||||
class StackForm extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Service $service;
|
||||
|
||||
public Collection $fields;
|
||||
|
|
@ -128,14 +131,20 @@ public function saveCompose($raw)
|
|||
|
||||
public function instantSave()
|
||||
{
|
||||
$this->syncData(true);
|
||||
$this->service->save();
|
||||
$this->dispatch('success', 'Service settings saved.');
|
||||
try {
|
||||
$this->authorize('update', $this->service);
|
||||
$this->syncData(true);
|
||||
$this->service->save();
|
||||
$this->dispatch('success', 'Service settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit($notify = true)
|
||||
{
|
||||
try {
|
||||
$this->authorize('update', $this->service);
|
||||
$this->validate();
|
||||
$this->syncData(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@
|
|||
namespace App\Livewire\Project\Shared;
|
||||
|
||||
use App\Models\Application;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class UploadConfig extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public $config;
|
||||
|
||||
public $applicationId;
|
||||
|
|
@ -29,13 +32,12 @@ public function mount()
|
|||
public function uploadConfig()
|
||||
{
|
||||
try {
|
||||
$application = Application::findOrFail($this->applicationId);
|
||||
$application = Application::ownedByCurrentTeam()->findOrFail($this->applicationId);
|
||||
$this->authorize('update', $application);
|
||||
$application->setConfig($this->config);
|
||||
$this->dispatch('success', 'Application settings updated');
|
||||
} catch (\Exception $e) {
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
|
||||
return;
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ public function checkUpdate()
|
|||
public function upgrade()
|
||||
{
|
||||
try {
|
||||
if (! isInstanceAdmin()) {
|
||||
abort(403);
|
||||
}
|
||||
if ($this->updateInProgress) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
Save
|
||||
</x-forms.button>
|
||||
@if (str($status)->startsWith('running'))
|
||||
<livewire:project.database.backup-now :backup="$backup" />
|
||||
<x-forms.button wire:click='backupNow'>Backup Now</x-forms.button>
|
||||
@endif
|
||||
@if ($backup->database_id !== 0)
|
||||
<x-modal-confirmation title="Confirm Backup Schedule Deletion?" buttonTitle="Delete Backups and Schedule"
|
||||
|
|
|
|||
Loading…
Reference in a new issue