Scope project user payloads to visible users
This commit is contained in:
@@ -59,9 +59,24 @@ class BuildProjectBacklogPayload
|
||||
)->resolve(),
|
||||
'users' => UserResource::collection(
|
||||
User::query()
|
||||
->whereIn('id', $this->visibleUserIds($project))
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
)->resolve(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private function visibleUserIds(Project $project): array
|
||||
{
|
||||
return User::query()
|
||||
->get()
|
||||
->filter(fn (User $candidate): bool => $this->permissionService->allows($candidate, Permission::ProjectRead, $project))
|
||||
->pluck('id')
|
||||
->map(fn (mixed $userId): int => (int) $userId)
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +116,24 @@ class BuildProjectTicketDetailPayload
|
||||
'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->get())->resolve(),
|
||||
'users' => UserResource::collection(
|
||||
User::query()
|
||||
->whereIn('id', $this->visibleUserIds($project))
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
)->resolve(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private function visibleUserIds(Project $project): array
|
||||
{
|
||||
return User::query()
|
||||
->get()
|
||||
->filter(fn (User $candidate): bool => $this->permissionService->allows($candidate, Permission::ProjectRead, $project))
|
||||
->pluck('id')
|
||||
->map(fn (mixed $userId): int => (int) $userId)
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Tests\Feature\Api;
|
||||
|
||||
use App\Actions\ProjectTicketMetadata\InitializeProjectTicketMetadata;
|
||||
use App\Models\Project;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
@@ -149,6 +150,43 @@ class SettingsApiTest extends TestCase
|
||||
->assertNoContent();
|
||||
}
|
||||
|
||||
public function test_project_payloads_only_return_users_who_can_view_the_project(): void
|
||||
{
|
||||
[$user, $project] = $this->projectContext();
|
||||
$viewer = User::factory()->create(['name' => 'Visible User']);
|
||||
User::factory()->create(['name' => 'Hidden User']);
|
||||
$teamViewer = User::factory()->create(['name' => 'Team Viewer']);
|
||||
$team = Team::factory()->create();
|
||||
|
||||
$this->syncProjectPermissions($project, $viewer, ['view']);
|
||||
$team->users()->attach($teamViewer);
|
||||
$this->syncProjectPermissions($project, $team, ['view']);
|
||||
|
||||
$ticket = $project->tickets()->firstOrCreate([
|
||||
'sequence_number' => 1,
|
||||
], [
|
||||
'ticket_key' => 'CORE-1',
|
||||
'ticket_priority_id' => $project->ticketPriorities()->firstOrFail()->id,
|
||||
'ticket_status_id' => $project->ticketStatuses()->firstOrFail()->id,
|
||||
'ticket_type_id' => $project->ticketTypes()->where('is_parent', false)->firstOrFail()->id,
|
||||
'title' => 'Scoped users ticket',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->getJson(route('api.projects.backlog', $project))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.users.0.name', 'Team Viewer')
|
||||
->assertJsonPath('data.users.1.name', 'Visible User')
|
||||
->assertJsonCount(2, 'data.users');
|
||||
|
||||
$this->actingAs($user)
|
||||
->getJson(route('api.projects.tickets.show', [$project, $ticket]))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.users.0.name', 'Team Viewer')
|
||||
->assertJsonPath('data.users.1.name', 'Visible User')
|
||||
->assertJsonCount(2, 'data.users');
|
||||
}
|
||||
|
||||
public function test_project_repositories_and_user_git_connections_are_available(): void
|
||||
{
|
||||
Http::preventStrayRequests();
|
||||
|
||||
Reference in New Issue
Block a user