PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.15
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.15
6.2.15 6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 All 197 releases
← All changes | app/Modules/Acl/Acl.php +378 -41 3.6.31 → 6.2.15 View file →
@@ -1,19 +1,80 @@
1 1 <?php
2 2
3 3 namespace FluentForm\App\Modules\Acl;
4 4
5 +use FluentForm\App\Services\Manager\FormManagerService;
6 +use FluentForm\Framework\Helpers\ArrayHelper;
7 +
5 8 class Acl
6 9 {
10 + public static $capability = '';
11 +
12 + public static $role = '';
13 +
14 + public static function normalizeFormId($formId)
15 + {
16 + if (null === $formId || false === $formId || '' === $formId) {
17 + return null;
18 + }
19 +
20 + if (is_string($formId)) {
21 + $formId = trim($formId);
22 + }
23 +
24 + if (!is_int($formId) && (!is_string($formId) || !ctype_digit($formId))) {
25 + return null;
26 + }
27 +
28 + $formId = (int) $formId;
29 +
30 + return $formId > 0 ? $formId : null;
31 + }
32 +
33 + public static function verifyFormId(
34 + $formId,
35 + $message = 'Invalid form id.',
36 + $json = true
37 + ) {
38 + $formId = static::normalizeFormId($formId);
39 +
40 + if ($formId) {
41 + return $formId;
42 + }
43 +
44 + if ($json) {
45 + wp_send_json_error([
46 + 'message' => $message,
47 + ], 422);
48 + }
49 +
50 + throw new \InvalidArgumentException(esc_html($message));
51 + }
52 +
7 53 public static function getPermissionSet()
8 54 {
9 - return apply_filters('fluentform_permission_set', [
10 - 'fluentform_full_access',
11 - 'fluentform_settings_manager',
55 + $data = [
12 56 'fluentform_dashboard_access',
13 57 'fluentform_forms_manager',
14 - 'fluentform_entries_viewer'
15 - ]);
58 + 'fluentform_entries_viewer',
59 + 'fluentform_manage_entries',
60 + 'fluentform_view_payments',
61 + 'fluentform_manage_payments',
62 + 'fluentform_settings_manager',
63 + 'fluentform_full_access',
64 + ];
65 +
66 + $data = apply_filters_deprecated(
67 + 'fluentform_permission_set',
68 + [
69 + $data,
70 + ],
71 + FLUENTFORM_FRAMEWORK_UPGRADE,
72 + 'fluentform/permission_set',
73 + 'Use fluentform/permission_set instead of fluentform_permission_set.'
74 + );
75 +
76 + return apply_filters('fluentform/permission_set', $data);
16 77 }
17 78
18 79 /**
19 80 * Fluentform access controll permissions assignment.
@@ -21,10 +82,20 @@
21 82 public static function setPermissions()
22 83 {
23 84 // Fire an event letting others know that fluentform
24 85 // is going to assign permission set to a role.
25 - do_action('before_fluentform_permission_set_assignment');
86 + do_action_deprecated(
87 + 'before_fluentform_permission_set_assignment',
88 + [
26 89
90 + ],
91 + FLUENTFORM_FRAMEWORK_UPGRADE,
92 + 'fluentform/before_permission_set_assignment',
93 + 'Use fluentform/before_permission_set_assignment instead of before_fluentform_permission_set_assignment.'
94 + );
95 +
96 + do_action('fluentform/before_permission_set_assignment');
97 +
27 98 // The permissions that fluentform supports altogether.
28 99 $permissions = self::getPermissionSet();
29 100
30 101 // The role that fluentform will use
@@ -30,16 +101,27 @@
30 101 // The role that fluentform will use
31 102 // to attach the permission set.
32 103 $role = get_role('administrator');
33 104
34 - // Looping through permission set to add to the role.
35 - foreach ($permissions as $permission) {
36 - $role->add_cap($permission);
105 + if ($role) {
106 + // Looping through permission set to add to the role.
107 + foreach ($permissions as $permission) {
108 + $role->add_cap($permission);
109 + }
37 110 }
38 111
39 112 // Fire an event letting others know that fluentform is
40 113 // done with the permission assignment to the role.
41 - do_action('after_fluentform_permission_set_assignment');
114 + do_action_deprecated(
115 + 'after_fluentform_permission_set_assignment',
116 + [
117 +
118 + ],
119 + FLUENTFORM_FRAMEWORK_UPGRADE,
120 + 'fluentform/after_permission_set_assignment',
121 + 'Use fluentform/after_permission_set_assignment instead of after_fluentform_permission_set_assignment.'
122 + );
123 + do_action('fluentform/after_permission_set_assignment');
42 124 }
43 125
44 126 /**
45 127 * Verify if current user has a fluentform permission.
@@ -44,11 +126,11 @@
44 126 /**
45 127 * Verify if current user has a fluentform permission.
46 128 *
47 129 * @param $permission
48 - * @param null $formId
130 + * @param null $formId
49 131 * @param string $message
50 - * @param bool $json
132 + * @param bool $json
51 133 *
52 134 * @throws \Exception
53 135 */
54 136 public static function verify(
@@ -55,65 +137,320 @@
55 137 $permission,
56 138 $formId = null,
57 139 $message = 'You do not have permission to perform this action.',
58 140 $json = true
59 - )
60 - {
61 - $allowed = self::hasPermission($permission, $formId);
141 + ) {
142 + static::verifyNonce();
143 +
144 + $allowed = static::hasPermission($permission, $formId);
145 +
62 146 if (!$allowed) {
63 147 if ($json) {
64 148 wp_send_json_error([
65 - 'message' => $message
149 + 'message' => $message,
66 150 ], 422);
67 151 } else {
68 - throw new \Exception($message);
152 + throw new \Exception(esc_html($message));
69 153 }
70 154 }
71 155 }
72 156
73 - public static function hasPermission($permission, $formId = false)
157 + public static function hasPermission($permissions, $formId = false)
74 158 {
75 - if (current_user_can('fluentform_full_access')) {
76 - return true;
159 + if ($formId && !FormManagerService::hasFormPermission($formId)) {
160 + return false;
77 161 }
78 162
79 - $allowed = current_user_can('fluentform_full_access');
80 - if ($allowed) {
163 + // Only explicit full-access users should bypass individual permission checks.
164 + if (static::hasExplicitFullAccess()) {
81 165 return true;
82 166 }
83 167
84 - if (is_array($permission)) {
85 - foreach ($permission as $eachPermission) {
86 - $allowed = current_user_can($eachPermission);
87 - if ($allowed) {
88 - return apply_filters('fluentform_verify_user_permission_' . $eachPermission, $allowed, $formId);
89 - } else {
90 - $isHookAllowed = apply_filters('fluentform_permission_callback', false, $eachPermission, $formId);
91 - if ($isHookAllowed) {
92 - return true;
93 - }
94 - }
168 + // Skip the role fallback for explicit managers, else a limited manager escalates.
169 + $grantedRole = self::isExplicitManager() ? false : static::getCurrentUserCapability();
170 +
171 + foreach ((array) $permissions as $permission) {
172 + $allowed = current_user_can($permission);
173 +
174 + // A granted role can satisfy scoped permissions, but never full access.
175 + if (!$allowed && $grantedRole && 'fluentform_full_access' !== $permission) {
176 + $allowed = true;
95 177 }
96 - return false;
178 +
179 + if (!$allowed) {
180 + continue;
181 + }
182 +
183 + return static::filterPermissionCheck($permission, $allowed, $formId);
97 184 }
98 185
99 - $allowed = current_user_can($permission);
100 - $allowed = apply_filters('fluentform_verify_user_permission_' . $permission, $allowed, $formId);
186 + return false;
187 + }
101 188
102 - if ($allowed) {
189 + private static function hasExplicitFullAccess()
190 + {
191 + return current_user_can('fluentform_full_access') || current_user_can('manage_options');
192 + }
193 +
194 + // Is the CURRENT user a Manager added by name (per-user), not just someone riding a delegated role?
195 + private static function isExplicitManager()
196 + {
197 + $userId = get_current_user_id();
198 +
199 + return (bool) ($userId && self::userHasDirectGrant($userId, wp_get_current_user()));
200 + }
201 +
202 + // "Direct grant" = permissions attached to the USER themselves (per-user Manager),
203 + // as opposed to access inherited from a delegated WordPress role.
204 + private static function userHasDirectGrant($userId, $user)
205 + {
206 + // Flag set when an admin adds the user via Settings -> Managers.
207 + if (get_user_meta($userId, '_fluent_forms_has_role', true)) {
103 208 return true;
104 209 }
105 210
106 - return apply_filters('fluentform_permission_callback', false, $permission, $formId);
211 + // Legacy fallback: caps stored on the user itself ($user->caps), not merged in from a role.
212 + foreach (static::getPermissionSet() as $permission) {
213 + if ($user && !empty($user->caps[$permission])) {
214 + return true;
215 + }
216 + }
217 +
218 + return false;
107 219 }
108 220
221 + private static function filterPermissionCheck($permission, $allowed, $formId)
222 + {
223 + $allowed = apply_filters_deprecated(
224 + 'fluentform_verify_user_permission_' . $permission,
225 + [
226 + $allowed,
227 + $formId,
228 + ],
229 + FLUENTFORM_FRAMEWORK_UPGRADE,
230 + 'fluentform/verify_user_permission_' . $permission,
231 + 'Use fluentform/verify_user_permission_' . $permission . ' instead of fluentform_verify_user_permission_' . $permission
232 + );
233 +
234 + return apply_filters('fluentform/verify_user_permission_' . $permission, $allowed, $formId);
235 + }
236 +
109 237 public static function hasAnyFormPermission($form_id = false)
110 238 {
111 - $allPermissions = self::getPermissionSet();
239 + $allPermissions = static::getPermissionSet();
240 +
112 241 foreach ($allPermissions as $permission) {
113 - if (self::hasPermission($permission, $form_id)) {
242 + if (static::hasPermission($permission, $form_id)) {
114 243 return true;
115 244 }
116 245 }
246 +
117 247 return false;
118 248 }
119 -}
249 +
250 + public static function getCurrentUserCapability()
251 + {
252 + if (static::$capability) {
253 + return static::$capability;
254 + }
255 +
256 + if (is_user_logged_in()) {
257 + static::$capability = static::findUserCapability(wp_get_current_user());
258 + } else {
259 + static::$capability = false;
260 + }
261 +
262 + return apply_filters('fluentform/current_user_capability', static::$capability);
263 + }
264 +
265 + public static function findUserCapability($user)
266 + {
267 + if (!$user) {
268 + return false;
269 + }
270 +
271 + if (static::isSuperMan($user)) {
272 + return 'manage_options';
273 + }
274 +
275 + $capabilities = get_option('_fluentform_form_permission');
276 +
277 + if (is_string($capabilities)) {
278 + $capabilities = (array) $capabilities;
279 + }
280 +
281 + if (!$capabilities) {
282 + return false;
283 + }
284 +
285 + foreach ($capabilities as $capability) {
286 + if ($user->has_cap($capability)) {
287 + return $capability;
288 + }
289 + }
290 +
291 + return false;
292 + }
293 +
294 + public static function getCurrentUserRole()
295 + {
296 + $user = wp_get_current_user();
297 +
298 + return static::$role = $user->roles[0];
299 + }
300 +
301 + public static function verifyNonce($key = 'fluent_forms_admin_nonce')
302 + {
303 + if (!wp_doing_ajax()) {
304 + return;
305 + }
306 +
307 + $nonce = wpFluentForm('request')->get($key);
308 +
309 + if (!wp_verify_nonce($nonce, $key)) {
310 + $message = __('Nonce verification failed, please try again.', 'fluentform');
311 + $message = apply_filters('fluentform/nonce_error', $message);
312 +
313 + wp_send_json_error([
314 + 'message' => $message,
315 + ], 422);
316 + }
317 + }
318 +
319 + public static function getReadablePermissions()
320 + {
321 + return [
322 + 'fluentform_dashboard_access' => [
323 + 'title' => __('View Forms', 'fluentform'),
324 + 'depends' => [],
325 + ],
326 + 'fluentform_forms_manager' => [
327 + 'title' => __('Manage Forms', 'fluentform'),
328 + 'depends' => [
329 + 'fluentform_dashboard_access',
330 + ],
331 + ],
332 + 'fluentform_entries_viewer' => [
333 + 'title' => __('View Entries', 'fluentform'),
334 + 'depends' => [
335 + 'fluentform_dashboard_access',
336 + ],
337 + ],
338 + 'fluentform_manage_entries' => [
339 + 'title' => __('Manage Entries', 'fluentform'),
340 + 'depends' => [
341 + 'fluentform_entries_viewer',
342 + ],
343 + ],
344 + 'fluentform_view_payments' => [
345 + 'title' => __('View Payments', 'fluentform'),
346 + 'depends' => [
347 + 'fluentform_dashboard_access',
348 + 'fluentform_entries_viewer',
349 + ],
350 + ],
351 + 'fluentform_manage_payments' => [
352 + 'title' => __('Manage Payments', 'fluentform'),
353 + 'depends' => [
354 + 'fluentform_view_payments',
355 + ],
356 + ],
357 + 'fluentform_settings_manager' => [
358 + 'title' => __('Manage Settings', 'fluentform'),
359 + 'depends' => [],
360 + ],
361 + 'fluentform_full_access' => [
362 + 'title' => __('Full Access', 'fluentform'),
363 + 'depends' => [],
364 + ],
365 + ];
366 + }
367 +
368 + public static function getUserPermissions($user = false)
369 + {
370 + if (is_numeric($user)) {
371 + $user = get_user_by('ID', $user);
372 + }
373 +
374 + if (!$user) {
375 + return [];
376 + }
377 +
378 + $permissionSet = static::getPermissionSet();
379 + $isSuperMan = static::isSuperMan($user);
380 + $capability = static::findUserCapability($user);
381 +
382 + $isManager = self::userHasDirectGrant($user->ID, $user);
383 +
384 + if ($isSuperMan) {
385 + return $permissionSet;
386 + }
387 +
388 + $userPermissions = array_values(array_intersect(array_keys($user->allcaps), $permissionSet));
389 +
390 + // Delegated-role users still return before the filter (unchanged boundary);
391 + // a manager just reports their own scoped caps instead of the full set.
392 + if ($capability) {
393 + return $isManager ? $userPermissions : $permissionSet;
394 + }
395 +
396 + return apply_filters('fluentform/current_user_permissions', $userPermissions);
397 + }
398 +
399 + public static function isSuperMan($user = false)
400 + {
401 + if ($user) {
402 + return $user->has_cap('manage_options');
403 + } else {
404 + return current_user_can('manage_options');
405 + }
406 + }
407 +
408 + public static function getCurrentUserPermissions()
409 + {
410 + return static::getUserPermissions(wp_get_current_user());
411 + }
412 +
413 + public static function attachPermissions($user, $permissions)
414 + {
415 + if (is_numeric($user)) {
416 + $user = get_user_by('ID', $user);
417 + }
418 +
419 + if (!$user) {
420 + return false;
421 + }
422 +
423 + if (user_can($user, 'manage_options')) {
424 + return $user;
425 + }
426 +
427 + $allPermissions = static::getPermissionSet();
428 +
429 + foreach ($allPermissions as $permission) {
430 + $user->remove_cap($permission);
431 + }
432 +
433 + $permissions = array_intersect($allPermissions, $permissions);
434 +
435 + foreach ($permissions as $permission) {
436 + $user->add_cap($permission);
437 + }
438 +
439 + /**
440 + * Fires after per-user FluentForm permissions are attached.
441 + *
442 + * Role-level changes already announce themselves via
443 + * fluentform/after_permission_set_assignment; this is the per-user
444 + * equivalent, so caches keyed on a user's effective permissions can be
445 + * invalidated when an individual manager is granted or revoked.
446 + *
447 + * @since 6.2.5
448 + *
449 + * @param \WP_User $user The user whose permissions changed.
450 + * @param array $permissions The permissions now attached.
451 + */
452 + do_action('fluentform/after_user_permissions_attached', $user, $permissions);
453 +
454 + return $user;
455 + }
456 +}