Cómo usar el atributo #[Requires]

Cuando escribe una aplicación web, a menudo se encuentra con la necesidad de restringir el acceso a ciertas partes de su aplicación. Quizás quiera que algunas peticiones solo puedan enviar datos mediante un formulario (es decir, con el método POST), o que sean accesibles solo para llamadas AJAX. En Nette Framework 3.2 apareció una nueva herramienta que le permitirá establecer tales restricciones de manera muy elegante y clara: el atributo #[Requires].

Un atributo es una marca especial en PHP que agrega antes de la definición de una clase o método. Como en realidad es una clase, para que los siguientes ejemplos funcionen, es necesario indicar la cláusula use:

use Nette\Application\Attributes\Requires;

Puede usar el atributo #[Requires] en la propia clase del presenter y también en estos métodos:

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

Los dos últimos métodos también se aplican a los componentes, por lo que también puede usar el atributo en ellos.

Si no se cumplen las condiciones que indica el atributo, se producirá un error HTTP 4xx.

Métodos HTTP

Puede especificar qué métodos HTTP (como GET, POST, etc.) están permitidos para el acceso. Por ejemplo, si desea permitir el acceso solo enviando un formulario, establezca:

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

¿Por qué debería usar POST en lugar de GET para acciones que cambian el estado y cómo hacerlo? Lea el tutorial.

Puede indicar un método o un array de métodos. Un caso especial es el valor '*', que permite todos los métodos, lo cual los presenters estándarmente no permiten por razones de seguridad.

Llamada AJAX

Si desea que el presenter o el método esté disponible solo para peticiones AJAX, use:

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

Mismo origen

Para aumentar la seguridad, puede requerir que la petición se realice desde el mismo dominio. Con esto evitará la vulnerabilidad CSRF:

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

Para los métodos handle<Signal>(), el acceso desde el mismo dominio se requiere automáticamente. Así que si, por el contrario, desea permitir el acceso desde cualquier dominio, indique:

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

Acceso a través de forward

A veces es útil restringir el acceso a un presenter para que esté disponible solo indirectamente, por ejemplo, usando el método forward() o switch() desde otro presenter. Así se protegen, por ejemplo, los error-presenters, para que no sea posible invocarlos desde la URL:

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

En la práctica, a menudo es necesario marcar ciertas vistas a las que solo se puede acceder en función de la lógica en el presenter. Es decir, nuevamente, para que no sea posible abrirlas directamente:

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

Acciones específicas

También puede restringir que cierto código, como la creación de un componente, esté disponible solo para acciones específicas en el presenter:

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

En caso de una sola acción, no es necesario escribir un array: #[Requires(actions: 'default')]

Atributos personalizados

Si desea usar el atributo #[Requires] repetidamente con la misma configuración, puede crear su propio atributo que herede #[Requires] y lo configure según sus necesidades.

Por ejemplo, #[SingleAction] permitirá el acceso solo a través de la acción default:

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

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

O #[RestMethods] permitirá el acceso a través de todos los métodos HTTP utilizados para la 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
{
}

Conclusión

El atributo #[Requires] le da una gran flexibilidad y control sobre cómo son accesibles sus páginas web. Usando reglas simples pero potentes, puede aumentar la seguridad y el correcto funcionamiento de su aplicación. Como puede ver, el uso de atributos en Nette no solo puede facilitar su trabajo, sino también asegurarlo.