Como usar o #[Requires] Atributo

Ao escrever um aplicativo da Web, você frequentemente se depara com a necessidade de restringir o acesso a determinadas partes do aplicativo. Talvez você queira que algumas solicitações só possam enviar dados por meio de um formulário (usando, portanto, o método POST) ou que sejam acessíveis somente a chamadas AJAX. No Nette Framework 3.2, foi introduzida uma nova ferramenta que permite que você defina essas restrições de forma elegante e clara: o atributo #[Requires] atributo.

O atributo é um marcador especial no PHP, que você adiciona antes da definição de uma classe ou método. Como ele é essencialmente uma classe, você precisa incluir a cláusula use para que os exemplos a seguir funcionem:

use Nette\Application\Attributes\Requires;

Você pode usar o atributo #[Requires] com a própria classe do apresentador e nesses métodos:

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

Os dois últimos métodos também se referem a componentes, portanto, você também pode usar o atributo com eles.

Se as condições especificadas pelo atributo não forem atendidas, será acionado um erro HTTP 4×x.

Métodos HTTP

Você pode especificar quais métodos HTTP (como GET, POST, etc.) são permitidos para acesso. Por exemplo, se você quiser permitir o acesso somente por meio do envio de um formulário, defina:

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

Por que você deve usar POST em vez de GET para ações de alteração de estado e como fazer isso? Leia o guia.

Você pode especificar um método ou uma matriz de métodos. Um caso especial é o valor '*' para ativar todos os métodos, que os apresentadores não permitem por padrão por motivos de segurança.

Chamadas AJAX

Se você quiser que um apresentador ou método seja acessível somente para solicitações AJAX, use:

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

Mesma origem

Para aumentar a segurança, você pode exigir que a solicitação seja feita a partir do mesmo domínio. Isso evita a vulnerabilidade ao CSRF:

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

Para os métodos handle<Signal>() o acesso do mesmo domínio é automaticamente necessário. Portanto, se você quiser permitir o acesso de qualquer domínio, especifique:

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

Acesso via Forward

Às vezes, é útil restringir o acesso a um apresentador para que ele esteja disponível apenas indiretamente, por exemplo, usando os métodos forward() ou switch() de outro apresentador. É assim que os apresentadores de erros são protegidos para evitar que sejam acionados a partir de um URL:

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

Na prática, muitas vezes é necessário marcar determinadas exibições que só podem ser acessadas com base na lógica do apresentador. Novamente, para que elas não possam ser abertas diretamente:

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

Ações específicas

Você também pode restringir o acesso a determinados códigos, como a criação de um componente, apenas para ações específicas no apresentador:

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

Para uma única ação, não há necessidade de escrever uma matriz: #[Requires(actions: 'default')]

Atributos personalizados

Se você quiser usar o atributo #[Requires] repetidamente com as mesmas configurações, você pode criar seu próprio atributo que herdará o atributo #[Requires] e defini-lo de acordo com suas necessidades.

Por exemplo, #[SingleAction] permite o acesso somente por meio da ação 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] permitirá o acesso por meio de todos os métodos HTTP usados para a 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
{
}

Conclusão

O atributo #[Requires] oferece grande flexibilidade e controle sobre como as páginas da Web são acessadas. Usando regras simples, porém poderosas, você pode aumentar a segurança e o funcionamento adequado do seu aplicativo. Como você pode ver, o uso de atributos no Nette pode não apenas simplificar seu trabalho, mas também torná-lo seguro.