Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
64.29% covered (warning)
64.29%
45 / 70
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
GetReportRequest
64.29% covered (warning)
64.29%
45 / 70
75.00% covered (warning)
75.00%
3 / 4
78.65
0.00% covered (danger)
0.00%
0 / 1
 authorize
53.70% covered (warning)
53.70%
29 / 54
0.00% covered (danger)
0.00%
0 / 1
87.02
 rules
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 prepareForValidation
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 getIdsAsArray
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Requests\v2\Reports;
4
5use App\Http\Models\Admin\CompanyGroup;
6use App\Http\Models\Auth\Role;
7use App\Http\Models\Auth\User;
8use Illuminate\Foundation\Http\FormRequest;
9
10class GetReportRequest extends FormRequest
11{
12    /**
13     * Determine if the user is authorized to make this request.
14     */
15    public function authorize(): bool
16    {
17        $user = $this->user();
18        $roles = $user->roles();
19
20        // 1. VENGRESO_ADMIN can do anything.
21        if (in_array(Role::VENGRESO_ADMIN, $roles)) {
22            return true;
23        }
24
25        $isCmc = $this->input('cmc', false);
26        if ($isCmc && ! in_array(Role::VENGRESO_ADMIN, $roles)) {
27            return false;
28        }
29
30        // All other roles must belong to a company.
31        $userCompanyId = $user->company_id;
32        if (empty($userCompanyId)) {
33            return false;
34        }
35
36        // 2. GLOBAL_ADMIN validation
37        if (in_array(Role::GLOBAL_ADMIN, $roles)) {
38            // They can only see data within their own company.
39            $requestedCompanyIds = $this->getIdsAsArray('company_ids');
40
41            // If they request specific companies, all must match their own.
42            if (! empty($requestedCompanyIds)) {
43                $unauthorizedIds = array_diff($requestedCompanyIds, [$userCompanyId]);
44
45                return empty($unauthorizedIds); // True if no unauthorized IDs were requested.
46            }
47
48            // If no company is specified, it's allowed. Controller will scope.
49            return true;
50        }
51
52        // 3. GROUP_ADMIN or REPORTING_ADMIN validation
53        if (in_array(Role::GROUP_ADMIN, $roles) || in_array(Role::REPORTING_ADMIN, $roles)) {
54            // First, ensure they are not trying to access other companies' data.
55            $requestedCompanyIds = $this->getIdsAsArray('company_ids');
56            if (! empty($requestedCompanyIds)) {
57                $unauthorizedIds = array_diff($requestedCompanyIds, [$userCompanyId]);
58                if (! empty($unauthorizedIds)) {
59                    return false; // Requesting companies they don't belong to.
60                }
61            }
62
63            $requestedGroupIds = $this->getIdsAsArray('group_ids');
64            $requestedSubgroupIds = $this->getIdsAsArray('subgroup_ids');
65            $requestedUserIds = $this->getIdsAsArray('user_ids');
66
67            // If they request specific groups, validate them.
68            if (! empty($requestedGroupIds)) {
69                $groups = CompanyGroup::whereIn('id', $requestedGroupIds)->get();
70                if ($groups->count() !== count($requestedGroupIds)) {
71                    return false; // An invalid group ID was requested.
72                }
73                foreach ($groups as $group) {
74                    if (! in_array($user->id, $group->admins ?? [])) {
75                        return false; // Not an admin of this group.
76                    }
77                }
78            }
79
80            // If they request specific groups, validate them.
81            if (! empty($requestedSubgroupIds)) {
82                $groups = CompanyGroup::whereIn('id', $requestedSubgroupIds)->get();
83                if ($groups->count() !== count($requestedSubgroupIds)) {
84                    return false; // An invalid group ID was requested.
85                }
86                foreach ($groups as $group) {
87                    if (! in_array($user->id, $group->admins ?? [])) {
88                        return false; // Not an admin of this group.
89                    }
90                }
91            }
92
93            // If they request specific users, validate them.
94            if (! empty($requestedUserIds)) {
95                $managedGroupIds = CompanyGroup::where('company_id', $userCompanyId)
96                    ->where('admins', $user->id)
97                    ->pluck('id')
98                    ->all();
99
100                if (empty($managedGroupIds)) {
101                    return false; // This admin manages no groups, so they can't view any users.
102                }
103
104                $usersToValidate = User::whereIn('id', $requestedUserIds)->get();
105                if ($usersToValidate->count() !== count($requestedUserIds)) {
106                    return false; // An invalid user ID was requested.
107                }
108
109                foreach ($usersToValidate as $userToValidate) {
110                    // This check assumes a user belongs to one group via `group_id`.
111                    // If a user can belong to many groups, this check needs to be adapted.
112                    if (! isset($userToValidate->group_id) || ! in_array($userToValidate->group_id, $managedGroupIds)) {
113                        return false; // This user is not in a group the admin manages.
114                    }
115                }
116            }
117
118            return true; // All checks passed.
119        }
120
121        // If user has no specific admin role, deny access.
122        return false;
123    }
124
125    /**
126     * Get the validation rules that apply to the request.
127     *
128     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
129     */
130    public function rules(): array
131    {
132        return [
133            'cmc' => 'sometimes|boolean',
134            'from' => 'sometimes|date',
135            'to' => 'sometimes|date|after_or_equal:from',
136            'company_ids' => 'sometimes|string',
137            'group_ids' => 'sometimes|string',
138            'subgroup_ids' => 'sometimes|string',
139            'user_ids' => 'sometimes|string',
140            'chart_ids' => 'sometimes|string',
141        ];
142    }
143
144    /**
145     * Prepare the data for validation.
146     *
147     * @return void
148     */
149    protected function prepareForValidation()
150    {
151        $user = $this->user();
152
153        // If a non-super-admin makes a request without specifying companyIds,
154        // we automatically scope the request to their own company.
155        if ($user && ! in_array(Role::VENGRESO_ADMIN, $user->roles()) && ! $this->has('companyIds') && $user->company_id) {
156            $this->merge([
157                'companyIds' => $user->company_id,
158            ]);
159        }
160    }
161
162    /**
163     * Helper method to process comma-separated ID strings from the request.
164     */
165    private function getIdsAsArray(string $key): array
166    {
167        return array_values(array_filter(explode(',', $this->input($key, ''))));
168    }
169}