refactor: move admin route into middleware group (#9225)

This commit is contained in:
Andras Bacsai 2026-03-28 14:18:16 +01:00 committed by GitHub
commit 91ab0b38d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 141 additions and 11 deletions

View file

@ -6,7 +6,6 @@
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Livewire\Component;
class Index extends Component
@ -22,16 +21,15 @@ class Index extends Component
public function mount()
{
if (! isCloud() && ! isDev()) {
return redirect()->route('dashboard');
}
if (Auth::id() !== 0 && ! session('impersonating')) {
return redirect()->route('dashboard');
abort(403);
}
$this->authorizeAdminAccess();
$this->getSubscribers();
}
public function back()
{
$this->authorizeAdminAccess();
if (session('impersonating')) {
session()->forget('impersonating');
$user = User::find(0);
@ -45,6 +43,7 @@ public function back()
public function submitSearch()
{
$this->authorizeAdminAccess();
if ($this->search !== '') {
$this->foundUsers = User::where(function ($query) {
$query->where('name', 'like', "%{$this->search}%")
@ -61,19 +60,33 @@ public function getSubscribers()
public function switchUser(int $user_id)
{
if (Auth::id() !== 0) {
return redirect()->route('dashboard');
}
$this->authorizeRootOnly();
session(['impersonating' => true]);
$user = User::find($user_id);
if (! $user) {
abort(404);
}
$team_to_switch_to = $user->teams->first();
// Cache::forget("team:{$user->id}");
Auth::login($user);
refreshSession($team_to_switch_to);
return redirect(request()->header('Referer'));
}
private function authorizeAdminAccess(): void
{
if (! Auth::check() || (Auth::id() !== 0 && ! session('impersonating'))) {
abort(403);
}
}
private function authorizeRootOnly(): void
{
if (! Auth::check() || Auth::id() !== 0) {
abort(403);
}
}
public function render()
{
return view('livewire.admin.index');

View file

@ -90,8 +90,6 @@
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
Route::get('/admin', AdminIndex::class)->name('admin.index');
Route::post('/forgot-password', [Controller::class, 'forgot_password'])->name('password.forgot')->middleware('throttle:forgot-password');
Route::get('/realtime', [Controller::class, 'realtime_test'])->middleware('auth');
Route::get('/verify', [Controller::class, 'verify'])->middleware('auth')->name('verify.email');
@ -109,6 +107,7 @@
});
Route::get('/', Dashboard::class)->name('dashboard');
Route::get('/admin', AdminIndex::class)->name('admin.index');
Route::get('/onboarding', BoardingIndex::class)->name('onboarding');
Route::get('/subscription', SubscriptionShow::class)->name('subscription.show');

View file

@ -0,0 +1,118 @@
<?php
use App\Livewire\Admin\Index as AdminIndex;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
uses(RefreshDatabase::class);
test('unauthenticated user cannot access admin route', function () {
$response = $this->get('/admin');
$response->assertRedirect('/login');
});
test('authenticated non-root user gets 403 on admin page', function () {
$team = Team::factory()->create();
$user = User::factory()->create();
$team->members()->attach($user->id, ['role' => 'admin']);
$this->actingAs($user);
session(['currentTeam' => ['id' => $team->id]]);
Livewire::test(AdminIndex::class)
->assertForbidden();
});
test('root user can access admin page in cloud mode', function () {
config()->set('constants.coolify.self_hosted', false);
$rootTeam = Team::find(0) ?? Team::factory()->create(['id' => 0]);
$rootUser = User::factory()->create(['id' => 0]);
$rootTeam->members()->attach($rootUser->id, ['role' => 'admin']);
$this->actingAs($rootUser);
session(['currentTeam' => ['id' => $rootTeam->id]]);
Livewire::test(AdminIndex::class)
->assertOk();
});
test('root user gets 403 on admin page in self-hosted non-dev mode', function () {
config()->set('constants.coolify.self_hosted', true);
config()->set('app.env', 'production');
$rootTeam = Team::find(0) ?? Team::factory()->create(['id' => 0]);
$rootUser = User::factory()->create(['id' => 0]);
$rootTeam->members()->attach($rootUser->id, ['role' => 'admin']);
$this->actingAs($rootUser);
session(['currentTeam' => ['id' => $rootTeam->id]]);
Livewire::test(AdminIndex::class)
->assertForbidden();
});
test('submitSearch requires admin authorization', function () {
$team = Team::factory()->create();
$user = User::factory()->create();
$team->members()->attach($user->id, ['role' => 'admin']);
$this->actingAs($user);
session(['currentTeam' => ['id' => $team->id]]);
Livewire::test(AdminIndex::class)
->assertForbidden();
});
test('switchUser requires root user id 0', function () {
config()->set('constants.coolify.self_hosted', false);
$rootTeam = Team::find(0) ?? Team::factory()->create(['id' => 0]);
$rootUser = User::factory()->create(['id' => 0]);
$rootTeam->members()->attach($rootUser->id, ['role' => 'admin']);
$targetUser = User::factory()->create();
$targetTeam = Team::factory()->create();
$targetTeam->members()->attach($targetUser->id, ['role' => 'admin']);
$this->actingAs($rootUser);
session(['currentTeam' => ['id' => $rootTeam->id]]);
Livewire::test(AdminIndex::class)
->assertOk()
->call('switchUser', $targetUser->id)
->assertRedirect();
});
test('switchUser rejects non-root user', function () {
config()->set('constants.coolify.self_hosted', false);
$team = Team::factory()->create();
$user = User::factory()->create();
$team->members()->attach($user->id, ['role' => 'admin']);
// Must set impersonating session to bypass mount() check
$this->actingAs($user);
session([
'currentTeam' => ['id' => $team->id],
'impersonating' => true,
]);
Livewire::test(AdminIndex::class)
->call('switchUser', 999)
->assertForbidden();
});
test('admin route has auth middleware applied', function () {
$route = collect(app('router')->getRoutes()->getRoutesByName())
->get('admin.index');
expect($route)->not->toBeNull();
$middleware = $route->gatherMiddleware();
expect($middleware)->toContain('auth');
});