refactor(auth): enforce team member authorization across app

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

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

No files matched your search

+4
View File
@@ -4,11 +4,14 @@ namespace App\Livewire\Project;
use App\Models\Project;
use App\Support\ValidationPatterns;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
class AddEmpty extends Component
{
use AuthorizesRequests;
public string $name;
public string $description = '';
@@ -29,6 +32,7 @@ class AddEmpty extends Component
public function submit()
{
try {
$this->authorize('create', Project::class);
$this->validate();
$project = Project::create([
'name' => $this->name,
@@ -123,6 +123,8 @@ class Show extends Component
public function downloadAllLogs(): string
{
$this->authorize('update', $this->application);
$logs = decode_remote_command_output($this->application_deployment_queue, includeAll: true)
->map(function ($line) {
$prefix = '';
@@ -6,11 +6,14 @@ use App\Enums\ApplicationDeploymentStatus;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use App\Models\Server;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Carbon;
use Livewire\Component;
class DeploymentNavbar extends Component
{
use AuthorizesRequests;
public ApplicationDeploymentQueue $application_deployment_queue;
public Application $application;
@@ -35,10 +38,15 @@ class DeploymentNavbar extends Component
public function show_debug()
{
$this->application->settings->is_debug_enabled = ! $this->application->settings->is_debug_enabled;
$this->application->settings->save();
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
$this->dispatch('refreshQueue');
try {
$this->authorize('update', $this->application);
$this->application->settings->is_debug_enabled = ! $this->application->settings->is_debug_enabled;
$this->application->settings->save();
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
$this->dispatch('refreshQueue');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function force_start()
+4
View File
@@ -11,11 +11,14 @@ use App\Models\Environment;
use App\Models\Project;
use App\Models\Server;
use App\Support\ValidationPatterns;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
class CloneMe extends Component
{
use AuthorizesRequests;
public string $project_uuid;
public string $environment_uuid;
@@ -91,6 +94,7 @@ class CloneMe extends Component
public function clone(string $type)
{
try {
$this->authorize('create', Project::class);
$this->validate([
'selectedDestination' => 'required',
'newName' => ValidationPatterns::nameRules(),
@@ -90,6 +90,7 @@ class Heading extends Component
$this->authorize('manage', $this->database);
$activity = RestartDatabase::run($this->database);
$this->js("window.dispatchEvent(new CustomEvent('startdatabase'))");
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class);
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -102,6 +103,7 @@ class Heading extends Component
$this->authorize('manage', $this->database);
$activity = StartDatabase::run($this->database);
$this->js("window.dispatchEvent(new CustomEvent('startdatabase'))");
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class);
} catch (\Throwable $e) {
return handleError($e, $this);
+4
View File
@@ -4,10 +4,13 @@ namespace App\Livewire\Project;
use App\Models\Project;
use App\Support\ValidationPatterns;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;
class Edit extends Component
{
use AuthorizesRequests;
public Project $project;
public string $name;
@@ -54,6 +57,7 @@ class Edit extends Component
public function submit()
{
try {
$this->authorize('update', $this->project);
$this->syncData(true);
$this->dispatch('success', 'Project updated.');
} catch (\Throwable $e) {
+4
View File
@@ -5,11 +5,14 @@ namespace App\Livewire\Project;
use App\Models\Application;
use App\Models\Project;
use App\Support\ValidationPatterns;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Locked;
use Livewire\Component;
class EnvironmentEdit extends Component
{
use AuthorizesRequests;
public Project $project;
public Application $application;
@@ -62,6 +65,7 @@ class EnvironmentEdit extends Component
public function submit()
{
try {
$this->authorize('update', $this->environment);
$this->syncData(true);
redirectRoute($this, 'project.environment.edit', [
'environment_uuid' => $this->environment->uuid,
+4
View File
@@ -105,6 +105,7 @@ class Heading extends Component
try {
$this->authorize('deploy', $this->service);
$activity = StartService::run($this->service, pullLatestImages: true);
$this->js("window.dispatchEvent(new CustomEvent('startservice'))");
$this->dispatch('activityMonitor', $activity->id);
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -125,6 +126,7 @@ class Heading extends Component
$activity->save();
}
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
$this->js("window.dispatchEvent(new CustomEvent('startservice'))");
$this->dispatch('activityMonitor', $activity->id);
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -152,6 +154,7 @@ class Heading extends Component
return;
}
$activity = StartService::run($this->service, stopBeforeStart: true);
$this->js("window.dispatchEvent(new CustomEvent('startservice'))");
$this->dispatch('activityMonitor', $activity->id);
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -169,6 +172,7 @@ class Heading extends Component
return;
}
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
$this->js("window.dispatchEvent(new CustomEvent('startservice'))");
$this->dispatch('activityMonitor', $activity->id);
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -72,7 +72,7 @@ class All extends Component
$query->orderBy('order');
}
return $query->get();
return $this->nullLockedValues($query->get());
}
public function getEnvironmentVariablesPreviewProperty()
@@ -86,7 +86,21 @@ class All extends Component
$query->orderBy('order');
}
return $query->get();
return $this->nullLockedValues($query->get());
}
private function nullLockedValues($envs)
{
$isMember = auth()->user()?->isMember();
$envs->each(function ($env) use ($isMember) {
if ($env->is_shown_once || $isMember) {
$env->value = null;
$env->real_value = null;
}
});
return $envs;
}
public function getDevView()
@@ -99,7 +113,12 @@ class All extends Component
private function formatEnvironmentVariables($variables)
{
return $variables->map(function ($item) {
$isMember = auth()->user()?->isMember();
return $variables->map(function ($item) use ($isMember) {
if ($isMember) {
return "$item->key=(Hidden, only admins can view)";
}
if ($item->is_shown_once) {
return "$item->key=(Locked Secret, delete and add again to change)";
}
@@ -52,6 +52,8 @@ class Show extends Component
public bool $is_redis_credential = false;
public bool $isValueHidden = false;
public array $problematicVariables = [];
protected $listeners = [
@@ -134,6 +136,13 @@ class Show extends Component
$this->is_really_required = $this->env->is_really_required ?? false;
$this->is_shared = $this->env->is_shared ?? false;
$this->real_value = $this->env->real_value;
if ($this->env->is_shown_once || auth()->user()?->isMember()) {
$this->value = null;
$this->real_value = null;
}
$this->isValueHidden = auth()->user()?->isMember() ?? false;
}
}