Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.31% covered (success)
92.31%
48 / 52
73.33% covered (warning)
73.33%
11 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
BasicPermission
92.31% covered (success)
92.31%
48 / 52
73.33% covered (warning)
73.33%
11 / 15
31.44
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 grantUserRight
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 getRightData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasPermission
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 getRightId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkUserRight
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllUserRights
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUserRightsCount
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getUserRights
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addRight
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
5.03
 checkRightData
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 renameRight
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 getAllRightsData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 refuseAllUserRights
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUserGroups
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * The basic permission class provides user rights.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Lars Tiedemann <php@larstiedemann.de>
12 * @copyright 2005-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2005-09-17
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Permission;
21
22use phpMyFAQ\Configuration;
23use phpMyFAQ\Core\Exception;
24use phpMyFAQ\Enums\PermissionType;
25use phpMyFAQ\User\CurrentUser;
26
27/**
28 * Class BasicPermission
29 *
30 * @package phpMyFAQ\Permission
31 */
32class BasicPermission implements PermissionInterface
33{
34    protected BasicPermissionRepository $repository;
35
36    public function __construct(
37        protected Configuration $configuration,
38    ) {
39        $this->repository = new BasicPermissionRepository($configuration);
40    }
41
42    /**
43     * Default right data stored when a new right is created.
44     *
45     * @var array<string, string|bool>
46     */
47    public array $defaultRightData = [
48        'name' => 'DEFAULT_RIGHT',
49        'description' => 'Short description.',
50        'for_users' => true,
51        'for_groups' => true,
52        'for_sections' => true,
53    ];
54
55    /**
56     * Gives the user a new user-right.
57     * Returns true on success, otherwise false.
58     *
59     * @param  int $userId  User ID
60     * @param  int $rightId Right ID
61     */
62    public function grantUserRight(int $userId, int $rightId): bool
63    {
64        $rightData = $this->getRightData($rightId);
65
66        if (!array_key_exists('for_users', $rightData)) {
67            return false;
68        }
69
70        return $this->repository->grantUserRight($userId, $rightId);
71    }
72
73    /**
74     * Returns an associative array with all data stored for in the
75     * database for the specified right. The keys of the returned
76     * array are the field names.
77     *
78     * @return array<string, mixed>
79     */
80    public function getRightData(int $rightId): array
81    {
82        return $this->repository->getRightData($rightId);
83    }
84
85    /**
86     * Returns true if the user given by user_id has the right,
87     * otherwise false. Unlike checkUserRight(), right may be a
88     * right-ID or a right-name. Another difference is that also
89     * group rights are taken into account.
90     *
91     * @param int   $userId User ID
92     * @param mixed $right  Right ID or right name
93     * @throws Exception
94     */
95    public function hasPermission(int $userId, mixed $right): bool
96    {
97        $currentUser = new CurrentUser($this->configuration);
98        $currentUser->getUserById($userId);
99
100        if ($currentUser->isSuperAdmin()) {
101            return true;
102        }
103
104        if (!is_numeric($right) && is_string($right)) {
105            $right = $this->getRightId($right);
106        }
107
108        if ($right instanceof PermissionType) {
109            $right = $this->getRightId($right->value);
110        }
111
112        return $this->checkUserRight($currentUser->getUserId(), (int) $right);
113    }
114
115    /**
116     * Returns the right-ID of the right with the name $name.
117     *
118     * @param string $name Name
119     */
120    public function getRightId(string $name): int
121    {
122        return $this->repository->getRightId($name);
123    }
124
125    /**
126     * Returns true if the user given by user_id has the right
127     * specified by right_id, otherwise false.
128     *
129     * @param int $userId  User ID
130     * @param int $rightId Right ID
131     */
132    public function checkUserRight(int $userId, int $rightId): bool
133    {
134        return $this->repository->checkUserRight($userId, $rightId);
135    }
136
137    /**
138     * Returns an array that contains the IDs of all user-rights
139     * the user owns.
140     *
141     * @param int $userId User ID
142     *
143     * @return array<int>
144     */
145    public function getAllUserRights(int $userId): array
146    {
147        return $this->getUserRights($userId);
148    }
149
150    /**
151     * Returns the number of user-rights the user specified by
152     * user_id owns.
153     *
154     * @param CurrentUser $currentUser User object
155     */
156    public function getUserRightsCount(CurrentUser $currentUser): int
157    {
158        $userRights = $this->getUserRights($currentUser->getUserId());
159
160        return is_countable($userRights) ? count($userRights) : 0;
161    }
162
163    /**
164     * Returns an array with the IDs of all user-rights the user
165     * specified by user_id owns. Group rights are not taken into
166     * account.
167     *
168     * @param int $userId User ID
169     *
170     * @return array<int>
171     */
172    public function getUserRights(int $userId): array
173    {
174        return $this->repository->getUserRights($userId);
175    }
176
177    /**
178     * Adds a new right into the database. Returns the ID of the
179     * new right. The associative array right_data contains the right
180     * data stored in the rights table.
181     *
182     * @param array<string, mixed> $rightData Array if rights
183     */
184    public function addRight(array $rightData): int
185    {
186        if ($this->getRightId((string) ($rightData['name'] ?? '')) > 0) {
187            return 0;
188        }
189
190        $nextId = $this->repository->nextRightId();
191        $checkedRightData = $this->checkRightData($rightData);
192        $rightRow = [];
193        foreach ($checkedRightData as $fieldName => $fieldValue) {
194            $rightRow[$fieldName] = is_int($fieldValue) ? $fieldValue : (string) $fieldValue;
195        }
196
197        if (!$this->repository->addRight($rightRow, $nextId)) {
198            return 0;
199        }
200
201        return $nextId;
202    }
203
204    /**
205     * Checks the given associative array $right_data. If a
206     * parameter is incorrect or is missing, it will be replaced
207     * by the default values in $this->default_right_data.
208     * Returns the corrected $right_data associative array.
209     *
210     * @param array<string, mixed> $rightData Array of rights
211     *
212     * @return array<string, mixed>
213     */
214    public function checkRightData(array $rightData): array
215    {
216        $stringFields = ['name', 'description'];
217        foreach ($stringFields as $field) {
218            if (array_key_exists($field, $rightData) && is_string($rightData[$field])) {
219                continue;
220            }
221
222            $rightData[$field] = $this->defaultRightData[$field];
223        }
224
225        $booleanLikeFields = ['for_users', 'for_groups', 'for_sections'];
226        foreach ($booleanLikeFields as $field) {
227            if (array_key_exists($field, $rightData)) {
228                continue;
229            }
230
231            $rightData[$field] = $this->defaultRightData[$field];
232        }
233
234        $rightData['for_users'] = (int) $rightData['for_users'];
235        $rightData['for_groups'] = (int) $rightData['for_groups'];
236        $rightData['for_sections'] = (int) $rightData['for_sections'];
237
238        return $rightData;
239    }
240
241    /**
242     * Renames rights, only used for updates.
243     */
244    public function renameRight(string $oldName, string $newName): bool
245    {
246        $rightId = $this->getRightId($oldName);
247        if ($rightId === 0) {
248            return false;
249        }
250
251        return $this->repository->renameRight($rightId, $newName);
252    }
253
254    /**
255     * Returns an array that contains all rights stored in the
256     * database. Each array element is an associative array with
257     * the complete right-data. By passing the optional parameter
258     * $order, the order of the array may be specified. Default is
259     * $order = 'right_id ASC'.
260     *
261     * @param string $order Ordering
262     *
263     * @return array<int, array>
264     */
265    public function getAllRightsData(string $order = 'ASC'): array
266    {
267        return $this->repository->getAllRightsData($order);
268    }
269
270    /**
271     * Refuses all user rights.
272     * Returns true on success, otherwise false.
273     *
274     * @param int $userId User ID
275     */
276    public function refuseAllUserRights(int $userId): bool
277    {
278        return $this->repository->refuseAllUserRights($userId);
279    }
280
281    /**
282     * Returns an array with the IDs of all groups the user belongs to.
283     * Since this is BasicPermission, always return an empty array.
284     *
285     * @param int $userId User ID
286     * @return array<int>
287     */
288    public function getUserGroups(int $userId): array
289    {
290        return [];
291    }
292}