Як використовувати атрибут #[Requires] Атрибут

При написанні веб-додатків часто виникає потреба обмежити доступ до певних частин вашого додатку. Можливо, ви хочете, щоб деякі запити могли надсилати дані лише через форму (використовуючи метод POST) або щоб вони були доступні лише для AJAX-викликів. У Nette Framework 3.2 з'явився новий інструмент, який дозволяє елегантно і зрозуміло встановлювати такі обмеження: атрибут #[Requires] атрибут

Атрибут – це спеціальний маркер в PHP, який ви додаєте перед визначенням класу або методу. Оскільки це, по суті, клас, вам потрібно включити речення use, щоб наведені нижче приклади працювали:

use Nette\Application\Attributes\Requires;

Ви можете використовувати атрибут #[Requires] з самим класом доповідача та з цими методами:

  • action<Action>()
  • render<View>()
  • handle<Signal>()
  • createComponent<Name>()

Останні два методи також стосуються компонентів, тому ви можете використовувати атрибут і з ними.

Якщо умови, визначені атрибутом, не виконуються, генерується помилка HTTP 4×x.

Методи HTTP

Ви можете вказати, які HTTP-методи (наприклад, GET, POST тощо) дозволені для доступу. Наприклад, якщо ви хочете дозволити доступ тільки за допомогою відправки форми, встановіть:

class AdminPresenter extends Nette\Application\UI\Presenter
{
	#[Requires(methods: 'POST')]
	public function actionDelete(int $id): void
	{
	}
}

Чому для зміни стану слід використовувати POST, а не GET, і як це зробити? Читайте гайд.

Ви можете вказати метод або масив методів. Особливим випадком є значення '*', щоб увімкнути всі методи, що не дозволяється за замовчуванням з міркувань безпеки.

AJAX-дзвінки

Якщо ви хочете, щоб доповідач або метод був доступний лише для AJAX-запитів, використовуйте цей параметр:

#[Requires(ajax: true)]
class AjaxPresenter extends Nette\Application\UI\Presenter
{
}

Одного походження

Щоб підвищити безпеку, ви можете вимагати, щоб запит був зроблений з того ж домену. Це запобігає вразливості до CSRF:

#[Requires(sameOrigin: true)]
class SecurePresenter extends Nette\Application\UI\Presenter
{
}

Для методів handle<Signal>() автоматично вимагається доступ з того ж домену. Тому, якщо ви хочете дозволити доступ з будь-якого домену, вкажіть це:

#[Requires(sameOrigin: false)]
public function handleList(): void
{
}

Доступ через Форвард

Іноді корисно обмежити доступ до презентера так, щоб він був доступний лише опосередковано, наприклад, за допомогою методів forward() або switch() з іншого презентера. Так захищаються презентери, що спричиняють помилки, щоб запобігти їхньому запуску з URL-адреси:

#[Requires(forward: true)]
class ForwardedPresenter extends Nette\Application\UI\Presenter
{
}

На практиці часто виникає потреба позначити певні подання, доступ до яких можна отримати лише на основі логіки в презентері. Знову ж таки, щоб їх не можна було відкрити безпосередньо:

class ProductPresenter extends Nette\Application\UI\Presenter
{

	public function actionDefault(int $id): void
	{
		$product = this->facade->getProduct($id);
		if (!product) {
			this->setView('notfound');
		}
	}

	#[Requires(forward: true)]
	public function renderNotFound(): void
	{
	}
}

Конкретні дії

Ви також можете обмежити доступ до певного коду, наприклад, створення компонента, лише для певних дій у презентері:

class EditDeletePresenter extends Nette\Application\UI\Presenter
{
	#[Requires(actions: ['add', 'edit'])]
	public function createComponentPostForm()
	{
	}
}

Для однієї дії не потрібно писати масив: #[Requires(actions: 'default')]

Користувацькі атрибути

Якщо ви хочете використовувати атрибут #[Requires] з тими самими налаштуваннями, ви можете створити власний атрибут, який успадкує #[Requires] і налаштувати його відповідно до ваших потреб.

Наприклад #[SingleAction] дозволяє доступ тільки через дію default:

#[Attribute]
class SingleAction extends Nette\Application\Attributes\Requires
{
	public function __construct()
	{
		parent::__construct(actions: 'default');
	}
}

#[SingleAction]
class SingleActionPresenter extends Nette\Application\UI\Presenter
{
}

Або #[RestMethods] дозволить доступ за допомогою всіх HTTP-методів, що використовуються для REST API:

#[\Attribute]
class RestMethods extends Nette\Application\Attributes\Requires
{
	public function __construct()
	{
		parent::__construct(methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
	}
}

#[RestMethods]
class ApiPresenter extends Nette\Application\UI\Presenter
{
}

Висновок

Атрибут #[Requires] надає вам велику гнучкість і контроль над тим, як здійснюється доступ до ваших веб-сторінок. Використовуючи прості, але потужні правила, ви можете підвищити безпеку та належне функціонування вашого додатку. Як бачите, використання атрибутів у Nette може не тільки спростити вашу роботу, але й убезпечити її.