Проверка прав

Чтобы предотвратить изменение настроек плагина или запретить просматривать данные, доступа к которым у пользователя нет, нужно проверять права пользователя, совершать какое-либо действие.

В WordPress для этого есть два подхода:

  1. проверка возможностей (прав) пользователя.
  2. одноразовые числа (nonce).

Это разные подходы и одноразовые числа, как правило дополняют проверку прав.

В этом разделе поговорим про первый: «Проверка прав юзера».

Возможности пользователя (проверка прав)

Проверка прав - это основной и пожалуй фундаментальный момент в защите и безопасности плагина. Он проверяет возможность пользователя делать что-либо. У каждого пользователя есть роль (Подписчик, Автор, Редактор) и у каждой роли есть набор прав. С помощью функции current_user_can() мы можем проверить имеет ли текущий пользователь указанное право, например, manage_options (право администратора изменять опции сайта). Если пользователь таким правом обладает, то функция вернет true и условие для выполнения какого-то кода будет выполнено и наоборот - если права нет, код выполняться не будет.

Например, «Автор» может публиковать и редактировать записи, но не имеет права редактировать чужие записи. А «Редактор» может публиковать и изменять свои и чужие записи. Но ни один из них не может изменять настройки сайта, как это может делать «Администратор». В зависимости от возможностей каждой роли, админ-панель WordPress будет выглядеть по-разному: например, у «Автора» будут одно меню, а у «Администратора» оно будет другое - значительно расширенно.

Список всех прав в зависимости от роли пользователя вы найдете в описании функции current_user_can().

Проверка прав пользователя на примере

При создании плагина, нужно уделять пристальное внимание ролям, которым должны быть разрешены выполнения определенных действий.

Например, давайте посмотрим на небезопасный код без проверки прав и что нехорошего в результате может получиться. Пример ниже показывает функцию, которая по задумке должна давать возможность «Редактору» удалять записи на фронтенде сайта:

## функция выводит ссылку на удаление текущей записи
function frontend_delete_link(){
	$url = add_query_arg(
		array( 'action'=> 'frontend_delete_link',  'post'=> get_the_ID() ),
		home_url(),
	);

	echo "<a href='{$url}'>Удалить</a>";
}

Теперь представим, мы использовали эту функцию и вывели ссылку где-то во фронте. При клике на нее будет срабатывать следующий код обработки запроса удаления записи:

## Функция удаляет указанную в $_REQUEST['post'] запись
function frontend_delete_post(){

	// определим ID записи
	$post_id = ( isset( $_REQUEST['post'] ) ?  get_post( (int) $_REQUEST['post'] ) : false;

	// Нет записи - выходим...
	if( empty($post_id) ) return;

	// Удаляем запись
	wp_trash_post( $post_id );

	// Перенаправляем на страницу админки
	$redirect = admin_url('edit.php');
	wp_safe_redirect( $redirect );
	exit;
}

// включаем обработчик
if( isset($_REQUEST['action']) && $_REQUEST['action']=='frontend_delete_link' ) {
	add_action('init', 'frontend_delete_post');
}

Код выше позволяет любому посетителю сайта, нажать на ссылку "Удалить" и удалить пост. А нужно, чтобы такую возможность имели юзеры с ролью «Редактор» или более старшей ролью, например, «Администратор». Если любой сможет это сделать, то вы в конечном итоге останетесь без контента.

Для этого давайте изменим код ссылки и не будет выводить ссылку для ролей младше редактора:

function frontend_delete_link(){
	// выходим если нет права 'edit_others_posts', которое есть у Редакторов и более старших ролей.
	if( ! current_user_can( 'edit_others_posts' ) ) return;

	$url = add_query_arg(
		array( 'action'=> 'frontend_delete_link',  'post'=> get_the_ID() ),
		home_url(),
	);

	echo "<a href='{$url}'>Удалить</a>";
}

Также такую проверку нужно добавить при выполнении самой операции удаления:

## Функция удаляет указанную в $_REQUEST['post'] запись
function frontend_delete_post(){

	// определим ID записи
	$post_id = ( isset( $_REQUEST['post'] ) ?  get_post( (int) $_REQUEST['post'] ) : false;

	// Нет записи - выходим...
	if( empty($post_id) ) return;

	// выходим если нет права 'edit_others_posts', которое есть у Редакторов и более старших ролей.
	if( ! current_user_can( 'edit_others_posts' ) ) return;

	// Удаляем запись
	wp_trash_post( $post_id );

	// Перенаправляем на страницу админки
	$redirect = admin_url('edit.php');
	wp_safe_redirect( $redirect );
	exit;
}

// включаем обработчик
if( isset($_REQUEST['action']) && $_REQUEST['action']=='frontend_delete_link' ) {
	add_action('init', 'frontend_delete_post');
}

В обоих случаях, мы подтверждаем, что у текущего пользователя есть возможность edit_others_posts, которая может быть только у «Редакторов» или ролей старше. Если такой возможности нет, мы ничего не делаем.

Логика проверки прав довольно проса и понятна. Вариантов где это нужно - очень много. И почти всегда используется функция current_user_can().

Одноразовые числа

Проверка прав - это первый и основной уровень защиты. Он разрешает или запрещает текущему пользователю заходить на страницы, видеть части контента и выполнять какие-либо действия. Но такая защита не идеальна... Почему? Читайте в разделе «Одноразовые числа».