Come utilizzare l'attributo #[Requires] Attributo

Quando si scrive un'applicazione web, spesso si incontra la necessità di limitare l'accesso a certe parti dell'applicazione. Forse si desidera che alcune richieste possano essere inviate solo tramite un modulo (utilizzando quindi il metodo POST) o che siano accessibili solo alle chiamate AJAX. In Nette Framework 3.2 è stato introdotto un nuovo strumento che consente di impostare tali restrizioni in modo elegante e chiaro: l'attributo #[Requires] attributo.

L'attributo è un marcatore speciale in PHP, che si aggiunge prima della definizione di una classe o di un metodo. Poiché si tratta essenzialmente di una classe, è necessario includere la clausola use per far funzionare gli esempi seguenti:

use Nette\Application\Attributes\Requires;

È possibile utilizzare l'attributo #[Requires] con la classe del presentatore e con questi metodi:

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

Anche gli ultimi due metodi riguardano i componenti, quindi è possibile utilizzare l'attributo anche con essi.

Se le condizioni specificate dall'attributo non sono soddisfatte, viene generato un errore HTTP 4×x.

Metodi HTTP

È possibile specificare quali metodi HTTP (come GET, POST, ecc.) sono consentiti per l'accesso. Ad esempio, se si desidera consentire l'accesso solo tramite l'invio di un modulo, impostare:

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

Perché usare POST invece di GET per le azioni di modifica dello stato e come farlo? Leggete la guida.

È possibile specificare un metodo o una serie di metodi. Un caso particolare è il valore '*' per abilitare tutti i metodi, che i presentatori non consentono di default per motivi di sicurezza.

Chiamate AJAX

Se si desidera che un presentatore o un metodo sia accessibile solo per le richieste AJAX, utilizzare:

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

Stessa origine

Per migliorare la sicurezza, è possibile richiedere che la richiesta venga effettuata dallo stesso dominio. In questo modo si evita la vulnerabilità al CSRF:

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

Per i metodi handle<Signal>() l'accesso dallo stesso dominio è automaticamente richiesto. Pertanto, se si desidera consentire l'accesso da qualsiasi dominio, specificare:

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

Accesso tramite Forward

A volte è utile limitare l'accesso a un presentatore in modo che sia disponibile solo indirettamente, ad esempio utilizzando i metodi forward() o switch() di un altro presentatore. In questo modo si proteggono i presentatori di errori, per evitare che vengano attivati da un URL:

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

In pratica, spesso è necessario contrassegnare alcune viste a cui si può accedere solo in base alla logica del presentatore. Anche in questo caso, in modo che non possano essere aperte direttamente:

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

Azioni specifiche

È anche possibile limitare l'accesso a determinati codici, come la creazione di un componente, solo per azioni specifiche nel presentatore:

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

Per una singola azione, non è necessario scrivere un array: #[Requires(actions: 'default')]

Attributi personalizzati

Se si desidera utilizzare l'attributo #[Requires] con le stesse impostazioni, è possibile creare un attributo personalizzato che erediterà #[Requires] e impostarlo secondo le proprie esigenze.

Ad esempio, #[SingleAction] consente l'accesso solo attraverso l'azione default:

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

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

Oppure #[RestMethods] consentirà l'accesso tramite tutti i metodi HTTP utilizzati per l'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
{
}

Conclusione

L'attributo #[Requires] offre grande flessibilità e controllo sulle modalità di accesso alle pagine web. Utilizzando regole semplici ma potenti, è possibile migliorare la sicurezza e il corretto funzionamento dell'applicazione. Come si può vedere, l'uso degli attributi in Nette non solo semplifica il lavoro, ma lo rende anche sicuro.