fix(auth): enforce authorization checks in Livewire components

- Replace manual ownership checks with authorize() in Destination/Show, NavbarDeleteTeam, and Project/Show
- Add authorization checks for team deletion and environment creation
- Add proper exception handling with try-catch blocks
- Add comprehensive feature and browser tests for authorization scenarios
- Update CLAUDE.md with Pest Browser Plugin testing guidelines
This commit is contained in:
Andras Bacsai committed 2026-02-25 16:38:04 +01:00
1 parent 41e1248b6f
commit 94dfd6a54e
8 files changed
+498 -27

No files matched your search

+4 -10
View File
@@ -2,7 +2,6 @@
namespace App\Livewire\Destination;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
@@ -32,17 +31,12 @@ class Show extends Component
$destination = StandaloneDocker::whereUuid($destination_uuid)->first() ??
SwarmDocker::whereUuid($destination_uuid)->firstOrFail();
$ownedByTeam = Server::ownedByCurrentTeam()->each(function ($server) use ($destination) {
if ($server->standaloneDockers->contains($destination) || $server->swarmDockers->contains($destination)) {
$this->destination = $destination;
$this->syncData();
}
});
if ($ownedByTeam === false) {
return redirect()->route('destination.index');
}
$this->authorize('view', $destination);
$this->destination = $destination;
$this->syncData();
} catch (\Illuminate\Auth\Access\AuthorizationException) {
abort(403);
} catch (\Throwable $e) {
return handleError($e, $this);
}
+29 -17
View File
@@ -2,12 +2,16 @@
namespace App\Livewire;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class NavbarDeleteTeam extends Component
{
use AuthorizesRequests;
public $team;
public function mount()
@@ -17,27 +21,35 @@ class NavbarDeleteTeam extends Component
public function delete($password)
{
if (! verifyPasswordConfirmation($password, $this)) {
return;
}
$currentTeam = currentTeam();
$currentTeam->delete();
$currentTeam->members->each(function ($user) use ($currentTeam) {
if ($user->id === Auth::id()) {
try {
if (! verifyPasswordConfirmation($password, $this)) {
return;
}
$user->teams()->detach($currentTeam);
$session = DB::table('sessions')->where('user_id', $user->id)->first();
if ($session) {
DB::table('sessions')->where('id', $session->id)->delete();
}
});
refreshSession();
$currentTeam = currentTeam();
$this->authorize('delete', $currentTeam);
return redirectRoute($this, 'team.index');
$currentTeam->members->each(function ($user) use ($currentTeam) {
if ($user->id === Auth::id()) {
return;
}
$user->teams()->detach($currentTeam);
$session = DB::table('sessions')->where('user_id', $user->id)->first();
if ($session) {
DB::table('sessions')->where('id', $session->id)->delete();
}
});
Cache::forget('user:'.Auth::id().':team:'.$currentTeam->id);
$currentTeam->delete();
$newTeam = Auth::user()->teams()->first();
refreshSession($newTeam);
return redirect()->route('team.index');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function render()
+4
View File
@@ -5,11 +5,14 @@ namespace App\Livewire\Project;
use App\Models\Environment;
use App\Models\Project;
use App\Support\ValidationPatterns;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
class Show extends Component
{
use AuthorizesRequests;
public Project $project;
public string $name;
@@ -41,6 +44,7 @@ class Show extends Component
public function submit()
{
try {
$this->authorize('create', Environment::class);
$this->validate();
$environment = Environment::create([
'name' => $this->name,