Files
coolify/tests/Feature/Authorization/TeamAuthorizationTest.php
T
Andras BacsaiandClaude Opus 5.5 a6a3b1d26b revert: undo the app changes from the test clean-up
Revert the app code changes of d666a0a95, 69eeb2f62, 1e66c9f9b and
a32082b03, so login, registration, OAuth, team danger zone, service
templates, and the other touched files are back to their earlier state.
Keep the removal of unused components and markup-only tests (d666a0a95
also contained those deletions by mistake).

StartupExecutionCleanupTest now fakes the template CDN with the current
file content, so app:init in the test no longer empties
templates/service-templates-latest.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:06:34 +02:00

371 lines
11 KiB
PHP

<?php
use App\Livewire\Team\DangerZone;
use App\Livewire\Team\Index as TeamIndex;
use App\Livewire\Team\Member as TeamMember;
use App\Models\InstanceSettings;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
uses(RefreshDatabase::class);
beforeEach(function () {
InstanceSettings::query()->delete();
$settings = new InstanceSettings;
$settings->id = 0;
$settings->save();
$this->team = Team::factory()->create(['personal_team' => false]);
$this->owner = User::factory()->create();
$this->owner->teams()->attach($this->team, ['role' => 'owner']);
$this->admin = User::factory()->create();
$this->admin->teams()->attach($this->team, ['role' => 'admin']);
$this->member = User::factory()->create();
$this->member->teams()->attach($this->team, ['role' => 'member']);
});
// --- Team Policy: update ---
test('owner can update team', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('update', $this->team))->toBeTrue();
});
test('admin can update team', function () {
$this->actingAs($this->admin);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('update', $this->team))->toBeTrue();
});
test('member cannot update team', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('update', $this->team))->toBeFalse();
});
// --- Team Policy: delete ---
test('owner can delete team', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('delete', $this->team))->toBeTrue();
});
test('admin cannot delete team', function () {
$this->actingAs($this->admin);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('delete', $this->team))->toBeFalse();
});
test('member cannot delete team', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('delete', $this->team))->toBeFalse();
});
// --- Team Policy: manageMembers ---
test('owner can manage team members', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('manageMembers', $this->team))->toBeTrue();
});
test('admin can manage team members', function () {
$this->actingAs($this->admin);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('manageMembers', $this->team))->toBeTrue();
});
test('member cannot manage team members', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('manageMembers', $this->team))->toBeFalse();
});
// --- Team Policy: manageInvitations ---
test('owner can manage invitations', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('manageInvitations', $this->team))->toBeTrue();
});
test('admin can manage invitations', function () {
$this->actingAs($this->admin);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('manageInvitations', $this->team))->toBeTrue();
});
test('member cannot manage invitations', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('manageInvitations', $this->team))->toBeFalse();
});
// --- Team Index Livewire: update ---
test('member cannot submit team settings via policy', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('update', $this->team))->toBeFalse();
});
test('owner can update team MCP setting', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
Livewire::test(TeamIndex::class)
->set('is_mcp_server_enabled', false)
->call('submit')
->assertDispatched('success');
expect($this->team->fresh()->is_mcp_server_enabled)->toBeFalse();
});
test('owner can update team build server fallback policy', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
Livewire::test(TeamIndex::class)
->set('is_build_server_fallback_enabled', false)
->call('submit')
->assertDispatched('success');
expect($this->team->fresh()->is_build_server_fallback_enabled)->toBeFalse();
});
test('team index mounts when is_mcp_server_enabled is null on the session team', function () {
$this->actingAs($this->owner);
// Simulate Team::create() without hydrating the DB default into session.
$this->team->setRawAttributes(array_merge(
$this->team->getAttributes(),
['is_mcp_server_enabled' => null],
));
session(['currentTeam' => $this->team]);
Livewire::test(TeamIndex::class)
->assertSuccessful()
->assertSet('is_mcp_server_enabled', true);
});
// --- Team Danger Zone Livewire: delete ---
test('member cannot delete team via danger zone', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
Livewire::test(DangerZone::class)
->call('delete')
->assertDispatched('error');
expect(Team::find($this->team->id))->not->toBeNull();
});
test('admin cannot delete team via policy', function () {
$this->actingAs($this->admin);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('delete', $this->team))->toBeFalse();
});
// --- Team Member Livewire: role changes ---
test('member cannot change roles via team member component', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
Livewire::test(TeamMember::class, ['member' => $this->admin])
->call('makeAdmin')
->assertDispatched('error');
});
test('member cannot remove team members', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
Livewire::test(TeamMember::class, ['member' => $this->admin])
->call('remove')
->assertDispatched('error');
});
// --- Invitations & Invite Link (policy checks — views wrapped in @can render empty for members) ---
test('member cannot manage invitations via policy', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('manageInvitations', $this->team))->toBeFalse();
});
test('owner can manage invitations via policy', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('manageInvitations', $this->team))->toBeTrue();
});
// --- Cross-team isolation ---
test('user from different team cannot update team', function () {
$otherTeam = Team::factory()->create();
$otherUser = User::factory()->create();
$otherUser->teams()->attach($otherTeam, ['role' => 'owner']);
$this->actingAs($otherUser);
session(['currentTeam' => $otherTeam]);
expect(auth()->user()->can('update', $this->team))->toBeFalse();
});
test('user from different team cannot manage members', function () {
$otherTeam = Team::factory()->create();
$otherUser = User::factory()->create();
$otherUser->teams()->attach($otherTeam, ['role' => 'owner']);
$this->actingAs($otherUser);
session(['currentTeam' => $otherTeam]);
expect(auth()->user()->can('manageMembers', $this->team))->toBeFalse();
});
// --- Team Policy: view ---
test('owner can view team', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('view', $this->team))->toBeTrue();
});
test('admin can view team', function () {
$this->actingAs($this->admin);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('view', $this->team))->toBeTrue();
});
test('member can view team', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('view', $this->team))->toBeTrue();
});
test('non-team member cannot view team', function () {
$outsider = User::factory()->create();
$this->actingAs($outsider);
session(['currentTeam' => $this->team]);
expect($outsider->can('view', $this->team))->toBeFalse();
});
// --- Team Policy: viewAdmin ---
test('owner can view admin panel', function () {
$this->actingAs($this->owner);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('viewAdmin', $this->team))->toBeTrue();
});
test('admin can view admin panel', function () {
$this->actingAs($this->admin);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('viewAdmin', $this->team))->toBeTrue();
});
test('member cannot view admin panel', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(auth()->user()->can('viewAdmin', $this->team))->toBeFalse();
});
test('non-team member cannot view admin panel', function () {
$outsider = User::factory()->create();
$this->actingAs($outsider);
session(['currentTeam' => $this->team]);
expect($outsider->can('viewAdmin', $this->team))->toBeFalse();
});
// --- Non-team member isolation ---
test('non-team member cannot update team', function () {
$outsider = User::factory()->create();
$this->actingAs($outsider);
session(['currentTeam' => $this->team]);
expect($outsider->can('update', $this->team))->toBeFalse();
});
test('non-team member cannot delete team', function () {
$outsider = User::factory()->create();
$this->actingAs($outsider);
session(['currentTeam' => $this->team]);
expect($outsider->can('delete', $this->team))->toBeFalse();
});
test('non-team member cannot manage members', function () {
$outsider = User::factory()->create();
$this->actingAs($outsider);
session(['currentTeam' => $this->team]);
expect($outsider->can('manageMembers', $this->team))->toBeFalse();
});
test('non-team member cannot manage invitations', function () {
$outsider = User::factory()->create();
$this->actingAs($outsider);
session(['currentTeam' => $this->team]);
expect($outsider->can('manageInvitations', $this->team))->toBeFalse();
});
// --- Team creation and model-level protection ---
test('member can create a new independent team', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
$newTeam = Team::create([
'name' => 'New Team',
'description' => 'Created by member',
'personal_team' => false,
]);
expect($newTeam)->toBeInstanceOf(Team::class)
->and($newTeam->name)->toBe('New Team');
});
test('member cannot update an existing team at model level', function () {
$this->actingAs($this->member);
session(['currentTeam' => $this->team]);
expect(fn () => $this->team->update(['name' => 'Hacked']))
->toThrow(Exception::class, 'You are not allowed to update this team.');
});