Comment utiliser l'attribut #[Requires]

Lorsque vous écrivez une application web, vous rencontrez souvent le besoin de restreindre l'accès à certaines parties de votre application. Peut-être souhaitez-vous que certaines requêtes ne puissent envoyer des données qu'à l'aide d'un formulaire (c'est-à-dire avec la méthode POST), ou qu'elles ne soient accessibles que pour les appels AJAX. Dans Nette Framework 3.2, un nouvel outil est apparu qui vous permet de définir de telles restrictions de manière très élégante et claire : l'attribut #[Requires].

Un attribut est une marque spéciale en PHP que vous ajoutez avant la définition d'une classe ou d'une méthode. Comme il s'agit en fait d'une classe, pour que les exemples suivants fonctionnent, il est nécessaire d'inclure la clause use :

use Nette\Application\Attributes\Requires;

Vous pouvez utiliser l'attribut #[Requires] sur la classe du presenter elle-même et également sur ces méthodes :

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

Les deux dernières méthodes concernent également les composants, vous pouvez donc également utiliser l'attribut avec eux.

Si les conditions spécifiées par l'attribut ne sont pas remplies, une erreur HTTP 4xx est levée.

Méthodes HTTP

Vous pouvez spécifier quelles méthodes HTTP (comme GET, POST, etc.) sont autorisées pour l'accès. Par exemple, si vous souhaitez autoriser l'accès uniquement en soumettant un formulaire, définissez :

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

Pourquoi devriez-vous utiliser POST au lieu de GET pour les actions modifiant l'état et comment faire ? Lisez le guide.

Vous pouvez spécifier une méthode ou un tableau de méthodes. Un cas spécial est la valeur '*', qui autorise toutes les méthodes, ce que les presenters par défaut n'autorisent pas pour des raisons de sécurité.

Appel AJAX

Si vous souhaitez que le presenter ou la méthode ne soit disponible que pour les requêtes AJAX, utilisez :

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

Même origine

Pour augmenter la sécurité, vous pouvez exiger que la requête soit effectuée depuis le même domaine. Cela empêche la vulnérabilité CSRF :

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

Pour les méthodes handle<Signal>(), l'accès depuis le même domaine est requis automatiquement. Donc, si au contraire vous souhaitez autoriser l'accès depuis n'importe quel domaine, spécifiez :

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

Accès via forward

Parfois, il est utile de restreindre l'accès à un presenter de manière à ce qu'il ne soit disponible qu'indirectement, par exemple en utilisant la méthode forward() ou switch() depuis un autre presenter. C'est ainsi que l'on protège par exemple les error-presenters, afin qu'ils ne puissent pas être appelés depuis une URL :

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

En pratique, il est souvent nécessaire de marquer certaines vues auxquelles on ne peut accéder qu'en fonction de la logique du presenter. Donc encore une fois, pour qu'elles ne puissent pas être ouvertes directement :

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
	{
	}
}

Actions spécifiques

Vous pouvez également restreindre l'accès à un certain code, comme la création d'un composant, pour qu'il ne soit disponible que pour des actions spécifiques dans le presenter :

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

Dans le cas d'une seule action, il n'est pas nécessaire d'écrire un tableau : #[Requires(actions: 'default')]

Attributs personnalisés

Si vous souhaitez utiliser l'attribut #[Requires] de manière répétée avec les mêmes paramètres, vous pouvez créer votre propre attribut qui héritera de #[Requires] et le configurera selon vos besoins.

Par exemple, #[SingleAction] permettra l'accès uniquement via l'action default :

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

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

Ou #[RestMethods] permettra l'accès via toutes les méthodes HTTP utilisées pour les API REST :

#[\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
{
}

Conclusion

L'attribut #[Requires] vous offre une grande flexibilité et un contrôle sur la manière dont vos pages web sont accessibles. À l'aide de règles simples mais puissantes, vous pouvez augmenter la sécurité et le bon fonctionnement de votre application. Comme vous pouvez le voir, l'utilisation des attributs dans Nette peut non seulement faciliter votre travail, mais aussi le sécuriser.