Issues (4967)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/wp-includes/capabilities.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Core User Role & Capabilities API
4
 *
5
 * @package WordPress
6
 * @subpackage Users
7
 */
8
9
/**
10
 * Map meta capabilities to primitive capabilities.
11
 *
12
 * This does not actually compare whether the user ID has the actual capability,
13
 * just what the capability or capabilities are. Meta capability list value can
14
 * be 'delete_user', 'edit_user', 'remove_user', 'promote_user', 'delete_post',
15
 * 'delete_page', 'edit_post', 'edit_page', 'read_post', or 'read_page'.
16
 *
17
 * @since 2.0.0
18
 *
19
 * @global array $post_type_meta_caps Used to get post type meta capabilities.
20
 *
21
 * @param string $cap       Capability name.
22
 * @param int    $user_id   User ID.
23
 * @param int    $object_id Optional. ID of the specific object to check against if `$cap` is a "meta" cap.
0 ignored issues
show
There is no parameter named $object_id. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
24
 *                          "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
25
 *                          by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
26
 *                          'edit_others_posts', etc. The parameter is accessed via func_get_args().
27
 * @return array Actual capabilities for meta capability.
28
 */
29
function map_meta_cap( $cap, $user_id ) {
30
	$args = array_slice( func_get_args(), 2 );
31
	$caps = array();
32
33
	switch ( $cap ) {
34
	case 'remove_user':
35
		// In multisite the user must be a super admin to remove themselves.
36
		if ( isset( $args[0] ) && $user_id == $args[0] && ! is_super_admin( $user_id ) ) {
37
			$caps[] = 'do_not_allow';
38
		} else {
39
			$caps[] = 'remove_users';
40
		}
41
		break;
42
	case 'promote_user':
43
	case 'add_users':
44
		$caps[] = 'promote_users';
45
		break;
46
	case 'edit_user':
47
	case 'edit_users':
48
		// Allow user to edit itself
49
		if ( 'edit_user' == $cap && isset( $args[0] ) && $user_id == $args[0] )
50
			break;
51
52
		// In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin.
53
		if ( is_multisite() && ( ( ! is_super_admin( $user_id ) && 'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id, 'manage_network_users' ) ) ) {
54
			$caps[] = 'do_not_allow';
55
		} else {
56
			$caps[] = 'edit_users'; // edit_user maps to edit_users.
57
		}
58
		break;
59
	case 'delete_post':
60
	case 'delete_page':
61
		$post = get_post( $args[0] );
62
		if ( ! $post ) {
63
			$caps[] = 'do_not_allow';
64
			break;
65
		}
66
67 View Code Duplication
		if ( 'revision' == $post->post_type ) {
68
			$post = get_post( $post->post_parent );
69
			if ( ! $post ) {
70
				$caps[] = 'do_not_allow';
71
				break;
72
			}
73
		}
74
75
		if ( ( get_option( 'page_for_posts' ) == $post->ID ) || ( get_option( 'page_on_front' ) == $post->ID ) ) {
76
			$caps[] = 'manage_options';
77
			break;
78
		}
79
80
		$post_type = get_post_type_object( $post->post_type );
81 View Code Duplication
		if ( ! $post_type ) {
82
			/* translators: 1: post type, 2: capability name */
83
			_doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
84
			$caps[] = 'edit_others_posts';
85
			break;
86
		}
87
88 View Code Duplication
		if ( ! $post_type->map_meta_cap ) {
89
			$caps[] = $post_type->cap->$cap;
90
			// Prior to 3.1 we would re-call map_meta_cap here.
91
			if ( 'delete_post' == $cap )
92
				$cap = $post_type->cap->$cap;
93
			break;
94
		}
95
96
		// If the post author is set and the user is the author...
97 View Code Duplication
		if ( $post->post_author && $user_id == $post->post_author ) {
98
			// If the post is published or scheduled...
99
			if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
100
				$caps[] = $post_type->cap->delete_published_posts;
101
			} elseif ( 'trash' == $post->post_status ) {
102
				$status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
103
				if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
104
					$caps[] = $post_type->cap->delete_published_posts;
105
				} else {
106
					$caps[] = $post_type->cap->delete_posts;
107
				}
108
			} else {
109
				// If the post is draft...
110
				$caps[] = $post_type->cap->delete_posts;
111
			}
112
		} else {
113
			// The user is trying to edit someone else's post.
114
			$caps[] = $post_type->cap->delete_others_posts;
115
			// The post is published or scheduled, extra cap required.
116
			if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
117
				$caps[] = $post_type->cap->delete_published_posts;
118
			} elseif ( 'private' == $post->post_status ) {
119
				$caps[] = $post_type->cap->delete_private_posts;
120
			}
121
		}
122
		break;
123
		// edit_post breaks down to edit_posts, edit_published_posts, or
124
		// edit_others_posts
125
	case 'edit_post':
126
	case 'edit_page':
127
		$post = get_post( $args[0] );
128
		if ( ! $post ) {
129
			$caps[] = 'do_not_allow';
130
			break;
131
		}
132
133 View Code Duplication
		if ( 'revision' == $post->post_type ) {
134
			$post = get_post( $post->post_parent );
135
			if ( ! $post ) {
136
				$caps[] = 'do_not_allow';
137
				break;
138
			}
139
		}
140
141
		$post_type = get_post_type_object( $post->post_type );
142 View Code Duplication
		if ( ! $post_type ) {
143
			/* translators: 1: post type, 2: capability name */
144
			_doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
145
			$caps[] = 'edit_others_posts';
146
			break;
147
		}
148
149 View Code Duplication
		if ( ! $post_type->map_meta_cap ) {
150
			$caps[] = $post_type->cap->$cap;
151
			// Prior to 3.1 we would re-call map_meta_cap here.
152
			if ( 'edit_post' == $cap )
153
				$cap = $post_type->cap->$cap;
154
			break;
155
		}
156
157
		// If the post author is set and the user is the author...
158 View Code Duplication
		if ( $post->post_author && $user_id == $post->post_author ) {
159
			// If the post is published or scheduled...
160
			if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
161
				$caps[] = $post_type->cap->edit_published_posts;
162
			} elseif ( 'trash' == $post->post_status ) {
163
				$status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
164
				if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
165
					$caps[] = $post_type->cap->edit_published_posts;
166
				} else {
167
					$caps[] = $post_type->cap->edit_posts;
168
				}
169
			} else {
170
				// If the post is draft...
171
				$caps[] = $post_type->cap->edit_posts;
172
			}
173
		} else {
174
			// The user is trying to edit someone else's post.
175
			$caps[] = $post_type->cap->edit_others_posts;
176
			// The post is published or scheduled, extra cap required.
177
			if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
178
				$caps[] = $post_type->cap->edit_published_posts;
179
			} elseif ( 'private' == $post->post_status ) {
180
				$caps[] = $post_type->cap->edit_private_posts;
181
			}
182
		}
183
		break;
184
	case 'read_post':
185
	case 'read_page':
186
		$post = get_post( $args[0] );
187
		if ( ! $post ) {
188
			$caps[] = 'do_not_allow';
189
			break;
190
		}
191
192 View Code Duplication
		if ( 'revision' == $post->post_type ) {
193
			$post = get_post( $post->post_parent );
194
			if ( ! $post ) {
195
				$caps[] = 'do_not_allow';
196
				break;
197
			}
198
		}
199
200
		$post_type = get_post_type_object( $post->post_type );
201 View Code Duplication
		if ( ! $post_type ) {
202
			/* translators: 1: post type, 2: capability name */
203
			_doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
204
			$caps[] = 'edit_others_posts';
205
			break;
206
		}
207
208 View Code Duplication
		if ( ! $post_type->map_meta_cap ) {
209
			$caps[] = $post_type->cap->$cap;
210
			// Prior to 3.1 we would re-call map_meta_cap here.
211
			if ( 'read_post' == $cap )
212
				$cap = $post_type->cap->$cap;
213
			break;
214
		}
215
216
		$status_obj = get_post_status_object( $post->post_status );
217
		if ( $status_obj->public ) {
218
			$caps[] = $post_type->cap->read;
219
			break;
220
		}
221
222
		if ( $post->post_author && $user_id == $post->post_author ) {
223
			$caps[] = $post_type->cap->read;
224
		} elseif ( $status_obj->private ) {
225
			$caps[] = $post_type->cap->read_private_posts;
226
		} else {
227
			$caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
228
		}
229
		break;
230
	case 'publish_post':
231
		$post = get_post( $args[0] );
232
		if ( ! $post ) {
233
			$caps[] = 'do_not_allow';
234
			break;
235
		}
236
237
		$post_type = get_post_type_object( $post->post_type );
238 View Code Duplication
		if ( ! $post_type ) {
239
			/* translators: 1: post type, 2: capability name */
240
			_doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
241
			$caps[] = 'edit_others_posts';
242
			break;
243
		}
244
245
		$caps[] = $post_type->cap->publish_posts;
246
		break;
247
	case 'edit_post_meta':
248
	case 'delete_post_meta':
249
	case 'add_post_meta':
250
	case 'edit_comment_meta':
251
	case 'delete_comment_meta':
252
	case 'add_comment_meta':
253
	case 'edit_term_meta':
254
	case 'delete_term_meta':
255
	case 'add_term_meta':
256
	case 'edit_user_meta':
257
	case 'delete_user_meta':
258
	case 'add_user_meta':
259
		list( $_, $object_type, $_ ) = explode( '_', $cap );
0 ignored issues
show
The assignment to $_ is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
260
		$object_id = (int) $args[0];
261
262
		switch ( $object_type ) {
263
			case 'post':
264
				$post = get_post( $object_id );
265
				if ( ! $post ) {
266
					break;
267
				}
268
269
				$sub_type = get_post_type( $post );
270
				break;
271
272
			case 'comment':
273
				$comment = get_comment( $object_id );
274
				if ( ! $comment ) {
275
					break;
276
				}
277
278
				$sub_type = empty( $comment->comment_type ) ? 'comment' : $comment->comment_type;
279
				break;
280
281
			case 'term':
282
				$term = get_term( $object_id );
283
				if ( ! $term ) {
284
					break;
285
				}
286
287
				$sub_type = $term->taxonomy;
288
				break;
289
290
			case 'user':
291
				$user = get_user_by( 'id', $object_id );
292
				if ( ! $user ) {
293
					break;
294
				}
295
296
				$sub_type = 'user';
297
				break;
298
		}
299
300
		if ( empty( $sub_type ) ) {
301
			$caps[] = 'do_not_allow';
302
			break;
303
		}
304
305
		$caps = map_meta_cap( "edit_{$object_type}", $user_id, $object_id );
306
307
		$meta_key = isset( $args[1] ) ? $args[1] : false;
308
309
		$has_filter = has_filter( "auth_{$object_type}_meta_{$meta_key}" ) || has_filter( "auth_{$object_type}_{$sub_type}_meta_{$meta_key}" );
310
		if ( $meta_key && $has_filter ) {
311
			/** This filter is documented in wp-includes/meta.php */
312
			$allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}", false, $meta_key, $object_id, $user_id, $cap, $caps );
313
314
			/** This filter is documented in wp-includes/meta.php */
315
			$allowed = apply_filters( "auth_{$object_type}_{$sub_type}_meta_{$meta_key}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
316
317
			if ( ! $allowed ) {
318
				$caps[] = $cap;
319
			}
320
		} elseif ( $meta_key && is_protected_meta( $meta_key, $object_type ) ) {
321
			$caps[] = $cap;
322
		}
323
		break;
324
	case 'edit_comment':
325
		$comment = get_comment( $args[0] );
326
		if ( ! $comment ) {
327
			$caps[] = 'do_not_allow';
328
			break;
329
		}
330
331
		$post = get_post( $comment->comment_post_ID );
332
333
		/*
334
		 * If the post doesn't exist, we have an orphaned comment.
335
		 * Fall back to the edit_posts capability, instead.
336
		 */
337
		if ( $post ) {
338
			$caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
339
		} else {
340
			$caps = map_meta_cap( 'edit_posts', $user_id );
341
		}
342
		break;
343 View Code Duplication
	case 'unfiltered_upload':
344
		if ( defined('ALLOW_UNFILTERED_UPLOADS') && ALLOW_UNFILTERED_UPLOADS && ( !is_multisite() || is_super_admin( $user_id ) )  )
345
			$caps[] = $cap;
346
		else
347
			$caps[] = 'do_not_allow';
348
		break;
349
	case 'edit_css' :
350 View Code Duplication
	case 'unfiltered_html' :
351
		// Disallow unfiltered_html for all users, even admins and super admins.
352
		if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
353
			$caps[] = 'do_not_allow';
354
		elseif ( is_multisite() && ! is_super_admin( $user_id ) )
355
			$caps[] = 'do_not_allow';
356
		else
357
			$caps[] = 'unfiltered_html';
358
		break;
359
	case 'edit_files':
360
	case 'edit_plugins':
361
	case 'edit_themes':
362
		// Disallow the file editors.
363
		if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
364
			$caps[] = 'do_not_allow';
365
		elseif ( ! wp_is_file_mod_allowed( 'capability_edit_themes' ) )
366
			$caps[] = 'do_not_allow';
367
		elseif ( is_multisite() && ! is_super_admin( $user_id ) )
368
			$caps[] = 'do_not_allow';
369
		else
370
			$caps[] = $cap;
371
		break;
372
	case 'update_plugins':
373
	case 'delete_plugins':
374
	case 'install_plugins':
375
	case 'upload_plugins':
376
	case 'update_themes':
377
	case 'delete_themes':
378
	case 'install_themes':
379
	case 'upload_themes':
380
	case 'update_core':
381
		// Disallow anything that creates, deletes, or updates core, plugin, or theme files.
382
		// Files in uploads are excepted.
383
		if ( ! wp_is_file_mod_allowed( 'capability_update_core' ) ) {
384
			$caps[] = 'do_not_allow';
385
		} elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
386
			$caps[] = 'do_not_allow';
387
		} elseif ( 'upload_themes' === $cap ) {
388
			$caps[] = 'install_themes';
389
		} elseif ( 'upload_plugins' === $cap ) {
390
			$caps[] = 'install_plugins';
391
		} else {
392
			$caps[] = $cap;
393
		}
394
		break;
395
	case 'activate_plugins':
396
		$caps[] = $cap;
397
		if ( is_multisite() ) {
398
			// update_, install_, and delete_ are handled above with is_super_admin().
399
			$menu_perms = get_site_option( 'menu_items', array() );
400
			if ( empty( $menu_perms['plugins'] ) )
401
				$caps[] = 'manage_network_plugins';
402
		}
403
		break;
404
	case 'delete_user':
405
	case 'delete_users':
406
		// If multisite only super admins can delete users.
407
		if ( is_multisite() && ! is_super_admin( $user_id ) )
408
			$caps[] = 'do_not_allow';
409
		else
410
			$caps[] = 'delete_users'; // delete_user maps to delete_users.
411
		break;
412
	case 'create_users':
413
		if ( !is_multisite() )
414
			$caps[] = $cap;
415
		elseif ( is_super_admin( $user_id ) || get_site_option( 'add_new_users' ) )
416
			$caps[] = $cap;
417
		else
418
			$caps[] = 'do_not_allow';
419
		break;
420
	case 'manage_links' :
421
		if ( get_option( 'link_manager_enabled' ) )
422
			$caps[] = $cap;
423
		else
424
			$caps[] = 'do_not_allow';
425
		break;
426
	case 'customize' :
427
		$caps[] = 'edit_theme_options';
428
		break;
429
	case 'delete_site':
430
		if ( is_multisite() ) {
431
			$caps[] = 'manage_options';
432
		} else {
433
			$caps[] = 'do_not_allow';
434
		}
435
		break;
436
	case 'edit_term':
437
	case 'delete_term':
438
	case 'assign_term':
439
		$term_id = (int) $args[0];
440
		$term = get_term( $term_id );
441
		if ( ! $term || is_wp_error( $term ) ) {
442
			$caps[] = 'do_not_allow';
443
			break;
444
		}
445
446
		$tax = get_taxonomy( $term->taxonomy );
447
		if ( ! $tax ) {
448
			$caps[] = 'do_not_allow';
449
			break;
450
		}
451
452
		if ( 'delete_term' === $cap && ( $term->term_id == get_option( 'default_' . $term->taxonomy ) ) ) {
453
			$caps[] = 'do_not_allow';
454
			break;
455
		}
456
457
		$taxo_cap = $cap . 's';
458
459
		$caps = map_meta_cap( $tax->cap->$taxo_cap, $user_id, $term_id );
460
461
		break;
462
	case 'manage_post_tags':
463
	case 'edit_categories':
464
	case 'edit_post_tags':
465
	case 'delete_categories':
466
	case 'delete_post_tags':
467
		$caps[] = 'manage_categories';
468
		break;
469
	case 'assign_categories':
470
	case 'assign_post_tags':
471
		$caps[] = 'edit_posts';
472
		break;
473
	case 'create_sites':
474
	case 'delete_sites':
475
	case 'manage_network':
476
	case 'manage_sites':
477
	case 'manage_network_users':
478
	case 'manage_network_plugins':
479
	case 'manage_network_themes':
480
	case 'manage_network_options':
481
	case 'upgrade_network':
482
		$caps[] = $cap;
483
		break;
484
	case 'setup_network':
485
		if ( is_multisite() ) {
486
			$caps[] = 'manage_network_options';
487
		} else {
488
			$caps[] = 'manage_options';
489
		}
490
		break;
491
	default:
492
		// Handle meta capabilities for custom post types.
493
		global $post_type_meta_caps;
494
		if ( isset( $post_type_meta_caps[ $cap ] ) ) {
495
			$args = array_merge( array( $post_type_meta_caps[ $cap ], $user_id ), $args );
496
			return call_user_func_array( 'map_meta_cap', $args );
497
		}
498
499
		// If no meta caps match, return the original cap.
500
		$caps[] = $cap;
501
	}
502
503
	/**
504
	 * Filters a user's capabilities depending on specific context and/or privilege.
505
	 *
506
	 * @since 2.8.0
507
	 *
508
	 * @param array  $caps    Returns the user's actual capabilities.
509
	 * @param string $cap     Capability name.
510
	 * @param int    $user_id The user ID.
511
	 * @param array  $args    Adds the context to the cap. Typically the object ID.
512
	 */
513
	return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );
514
}
515
516
/**
517
 * Whether the current user has a specific capability.
518
 *
519
 * While checking against particular roles in place of a capability is supported
520
 * in part, this practice is discouraged as it may produce unreliable results.
521
 *
522
 * Note: Will always return true if the current user is a super admin, unless specifically denied.
523
 *
524
 * @since 2.0.0
525
 *
526
 * @see WP_User::has_cap()
527
 * @see map_meta_cap()
528
 *
529
 * @param string $capability Capability name.
530
 * @param int    $object_id  Optional. ID of the specific object to check against if `$capability` is a "meta" cap.
0 ignored issues
show
There is no parameter named $object_id. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
531
 *                           "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
532
 *                           by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
533
 *                           'edit_others_posts', etc. Accessed via func_get_args() and passed to WP_User::has_cap(),
534
 *                           then map_meta_cap().
535
 * @return bool Whether the current user has the given capability. If `$capability` is a meta cap and `$object_id` is
536
 *              passed, whether the current user has the given meta capability for the given object.
537
 */
538
function current_user_can( $capability ) {
539
	$current_user = wp_get_current_user();
540
541
	if ( empty( $current_user ) )
542
		return false;
543
544
	$args = array_slice( func_get_args(), 1 );
545
	$args = array_merge( array( $capability ), $args );
546
547
	return call_user_func_array( array( $current_user, 'has_cap' ), $args );
548
}
549
550
/**
551
 * Whether current user has a capability or role for a given site.
552
 *
553
 * @since 3.0.0
554
 *
555
 * @param int    $blog_id    Site ID.
556
 * @param string $capability Capability or role name.
557
 * @return bool
558
 */
559
function current_user_can_for_blog( $blog_id, $capability ) {
560
	$switched = is_multisite() ? switch_to_blog( $blog_id ) : false;
561
562
	$current_user = wp_get_current_user();
563
564
	if ( empty( $current_user ) ) {
565
		if ( $switched ) {
566
			restore_current_blog();
567
		}
568
		return false;
569
	}
570
571
	$args = array_slice( func_get_args(), 2 );
572
	$args = array_merge( array( $capability ), $args );
573
574
	$can = call_user_func_array( array( $current_user, 'has_cap' ), $args );
575
576
	if ( $switched ) {
577
		restore_current_blog();
578
	}
579
580
	return $can;
581
}
582
583
/**
584
 * Whether author of supplied post has capability or role.
585
 *
586
 * @since 2.9.0
587
 *
588
 * @param int|object $post Post ID or post object.
589
 * @param string $capability Capability or role name.
590
 * @return bool
591
 */
592 View Code Duplication
function author_can( $post, $capability ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
593
	if ( !$post = get_post($post) )
594
		return false;
595
596
	$author = get_userdata( $post->post_author );
597
598
	if ( ! $author )
599
		return false;
600
601
	$args = array_slice( func_get_args(), 2 );
602
	$args = array_merge( array( $capability ), $args );
603
604
	return call_user_func_array( array( $author, 'has_cap' ), $args );
605
}
606
607
/**
608
 * Whether a particular user has capability or role.
609
 *
610
 * @since 3.1.0
611
 *
612
 * @param int|object $user User ID or object.
613
 * @param string $capability Capability or role name.
614
 * @return bool
615
 */
616 View Code Duplication
function user_can( $user, $capability ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
617
	if ( ! is_object( $user ) )
618
		$user = get_userdata( $user );
619
620
	if ( ! $user || ! $user->exists() )
621
		return false;
622
623
	$args = array_slice( func_get_args(), 2 );
624
	$args = array_merge( array( $capability ), $args );
625
626
	return call_user_func_array( array( $user, 'has_cap' ), $args );
627
}
628
629
/**
630
 * Retrieves the global WP_Roles instance and instantiates it if necessary.
631
 *
632
 * @since 4.3.0
633
 *
634
 * @global WP_Roles $wp_roles WP_Roles global instance.
635
 *
636
 * @return WP_Roles WP_Roles global instance if not already instantiated.
637
 */
638
function wp_roles() {
639
	global $wp_roles;
640
641
	if ( ! isset( $wp_roles ) ) {
642
		$wp_roles = new WP_Roles();
643
	}
644
	return $wp_roles;
645
}
646
647
/**
648
 * Retrieve role object.
649
 *
650
 * @since 2.0.0
651
 *
652
 * @param string $role Role name.
653
 * @return WP_Role|null WP_Role object if found, null if the role does not exist.
654
 */
655
function get_role( $role ) {
656
	return wp_roles()->get_role( $role );
657
}
658
659
/**
660
 * Add role, if it does not exist.
661
 *
662
 * @since 2.0.0
663
 *
664
 * @param string $role Role name.
665
 * @param string $display_name Display name for role.
666
 * @param array $capabilities List of capabilities, e.g. array( 'edit_posts' => true, 'delete_posts' => false );
667
 * @return WP_Role|null WP_Role object if role is added, null if already exists.
668
 */
669
function add_role( $role, $display_name, $capabilities = array() ) {
670
	if ( empty( $role ) ) {
671
		return;
672
	}
673
	return wp_roles()->add_role( $role, $display_name, $capabilities );
674
}
675
676
/**
677
 * Remove role, if it exists.
678
 *
679
 * @since 2.0.0
680
 *
681
 * @param string $role Role name.
682
 */
683
function remove_role( $role ) {
684
	wp_roles()->remove_role( $role );
685
}
686
687
/**
688
 * Retrieve a list of super admins.
689
 *
690
 * @since 3.0.0
691
 *
692
 * @global array $super_admins
693
 *
694
 * @return array List of super admin logins
695
 */
696
function get_super_admins() {
697
	global $super_admins;
698
699
	if ( isset($super_admins) )
700
		return $super_admins;
701
	else
702
		return get_site_option( 'site_admins', array('admin') );
703
}
704
705
/**
706
 * Determine if user is a site admin.
707
 *
708
 * @since 3.0.0
709
 *
710
 * @param int $user_id (Optional) The ID of a user. Defaults to the current user.
711
 * @return bool True if the user is a site admin.
712
 */
713
function is_super_admin( $user_id = false ) {
714
	if ( ! $user_id || $user_id == get_current_user_id() )
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_id of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
715
		$user = wp_get_current_user();
716
	else
717
		$user = get_userdata( $user_id );
718
719
	if ( ! $user || ! $user->exists() )
720
		return false;
721
722
	if ( is_multisite() ) {
723
		$super_admins = get_super_admins();
724
		if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) )
725
			return true;
726
	} else {
727
		if ( $user->has_cap('delete_users') )
728
			return true;
729
	}
730
731
	return false;
732
}
733
734
/**
735
 * Grants Super Admin privileges.
736
 *
737
 * @since 3.0.0
738
 *
739
 * @global array $super_admins
740
 *
741
 * @param int $user_id ID of the user to be granted Super Admin privileges.
742
 * @return bool True on success, false on failure. This can fail when the user is
743
 *              already a super admin or when the `$super_admins` global is defined.
744
 */
745
function grant_super_admin( $user_id ) {
746
	// If global super_admins override is defined, there is nothing to do here.
747
	if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) {
748
		return false;
749
	}
750
751
	/**
752
	 * Fires before the user is granted Super Admin privileges.
753
	 *
754
	 * @since 3.0.0
755
	 *
756
	 * @param int $user_id ID of the user that is about to be granted Super Admin privileges.
757
	 */
758
	do_action( 'grant_super_admin', $user_id );
759
760
	// Directly fetch site_admins instead of using get_super_admins()
761
	$super_admins = get_site_option( 'site_admins', array( 'admin' ) );
762
763
	$user = get_userdata( $user_id );
764 View Code Duplication
	if ( $user && ! in_array( $user->user_login, $super_admins ) ) {
765
		$super_admins[] = $user->user_login;
766
		update_site_option( 'site_admins' , $super_admins );
767
768
		/**
769
		 * Fires after the user is granted Super Admin privileges.
770
		 *
771
		 * @since 3.0.0
772
		 *
773
		 * @param int $user_id ID of the user that was granted Super Admin privileges.
774
		 */
775
		do_action( 'granted_super_admin', $user_id );
776
		return true;
777
	}
778
	return false;
779
}
780
781
/**
782
 * Revokes Super Admin privileges.
783
 *
784
 * @since 3.0.0
785
 *
786
 * @global array $super_admins
787
 *
788
 * @param int $user_id ID of the user Super Admin privileges to be revoked from.
789
 * @return bool True on success, false on failure. This can fail when the user's email
790
 *              is the network admin email or when the `$super_admins` global is defined.
791
 */
792
function revoke_super_admin( $user_id ) {
793
	// If global super_admins override is defined, there is nothing to do here.
794
	if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) {
795
		return false;
796
	}
797
798
	/**
799
	 * Fires before the user's Super Admin privileges are revoked.
800
	 *
801
	 * @since 3.0.0
802
	 *
803
	 * @param int $user_id ID of the user Super Admin privileges are being revoked from.
804
	 */
805
	do_action( 'revoke_super_admin', $user_id );
806
807
	// Directly fetch site_admins instead of using get_super_admins()
808
	$super_admins = get_site_option( 'site_admins', array( 'admin' ) );
809
810
	$user = get_userdata( $user_id );
811
	if ( $user && 0 !== strcasecmp( $user->user_email, get_site_option( 'admin_email' ) ) ) {
812 View Code Duplication
		if ( false !== ( $key = array_search( $user->user_login, $super_admins ) ) ) {
813
			unset( $super_admins[$key] );
814
			update_site_option( 'site_admins', $super_admins );
815
816
			/**
817
			 * Fires after the user's Super Admin privileges are revoked.
818
			 *
819
			 * @since 3.0.0
820
			 *
821
			 * @param int $user_id ID of the user Super Admin privileges were revoked from.
822
			 */
823
			do_action( 'revoked_super_admin', $user_id );
824
			return true;
825
		}
826
	}
827
	return false;
828
}
829