Jak používat atribut #[Requires]

Když píšete webovou aplikaci, často se setkáte s potřebou omezit přístup k určitým částem vaší aplikace. Možná chcete, aby některé požadavky mohly odesílat data pouze pomocí formuláře (tedy metodou POST), nebo aby byly přístupné pouze pro AJAXové volání. V Nette Frameworku 3.2 se objevil nový nástroj, který vám umožní taková omezení nastavit velmi elegantně a přehledně: atribut #[Requires].

Atribut je speciální značka v PHP, kterou přidáte před definici třídy nebo metody. Protože jde vlastně o třídu, aby vám následující příklady fungovaly, je nutné uvést klauzuli use:

use Nette\Application\Attributes\Requires;

Atribut #[Requires] můžete použít u samotné třídy presenteru a také na těchto metodách:

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

Poslední dvě metody se týkají i komponent, tedy atribut můžete používat i u nich.

Pokud nejsou splněny podmínky, které atribut uvádí, dojde k vyvolání HTTP chyby 4xx.

Metody HTTP

Můžete specifikovat, které HTTP metody (jako GET, POST atd.) jsou pro přístup povolené. Například, pokud chcete povolit přístup pouze odesíláním formuláře, nastavíte:

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

Proč byste měli používat POST místo GET pro akce měnící stav a jak na to? Přečtěte si návod.

Můžete uvést metodu nebo pole metod. Speciálním případem je hodnota '*', která povolí všechny metody, což standardně presentery z bezpečnostních důvodů nedovolují.

AJAXové volání

Pokud chcete, aby byl presenter nebo metoda dostupná pouze pro AJAXové požadavky, použijte:

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

Stejný původ

Pro zvýšení bezpečnosti můžete vyžadovat, aby byl požadavek učiněn ze stejné domény. Tím zabráníte zranitelnosti CSRF:

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

U metod handle<Signal>() je přístup ze stejné domény vyžadován automaticky. Takže pokud naopak chcete povolit přístup z jakékoliv domény, uveďte:

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

Přístup přes forward

Někdy je užitečné omezit přístup k presenteru tak, aby byl dostupný pouze nepřímo, například použitím metody forward() nebo switch() z jiného presenteru. Takto se třeba chrání error-presentery, aby je nebylo možné vyvolat z URL:

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

V praxi bývá často potřeba označit určité views, ke kterým se lze dostat až na základě logiky v presenteru. Tedy opět, aby je nebylo možné otevřít přímo:

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

Konkrétní akce

Můžete také omezit, že určitý kód, třeba vytvoření komponenty, bude dostupné pouze pro specifické akce v presenteru:

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

V případě jedné akce není potřeba zapisovat pole: #[Requires(actions: 'default')]

Vlastní atributy

Pokud chcete použít atribut #[Requires] opakovaně s týmž nastavením, můžete si vytvořit vlastní atribut, který bude dědit #[Requires] a nastaví ho podle potřeb.

Například #[SingleAction] umožní přístup pouze přes akci default:

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

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

Nebo #[RestMethods] umožní přístup přes všechny HTTP metody používané pro 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
{
}

Závěr

Atribut #[Requires] vám dává velkou flexibilitu a kontrolu nad tím, jak jsou vaše webové stránky přístupné. Pomocí jednoduchých, ale mocných pravidel můžete zvýšit bezpečnost a správné fungování vaší aplikace. Jak vidíte, použití atributů v Nette může vaši práci nejen usnadnit, ale i zabezpečit.