Jak używać atrybutu #[Requires]
Pisząc aplikację webową, często napotykasz potrzebę ograniczenia dostępu do niektórych jej części. Może
chcesz, żeby pewne żądania mogły przesyłać dane tylko formularzem (czyli metodą POST) albo żeby były dostępne
wyłącznie dla wywołań AJAX-owych. W Nette Framework 3.2 pojawiło się nowe narzędzie, które pozwala takie ograniczenia
ustawiać elegancko i przejrzyście: atrybut #[Requires].
Atrybut to specjalny znacznik w PHP, który dodajesz przed definicją klasy albo metody. Ponieważ w istocie jest to klasa,
żeby poniższe przykłady działały, musisz dodać klauzulę use:
use Nette\Application\Attributes\Requires;
Atrybutu #[Requires] możesz użyć na samej klasie presentera oraz na tych metodach:
action<Action>()render<View>()handle<Signal>()createComponent<Name>()
Dwie ostatnie metody dotyczą także komponentów, więc możesz atrybutu używać również w nich.
Jeśli warunki podane w atrybucie nie są spełnione, zostaje wywołany błąd HTTP 4xx.
Metody HTTP
Możesz określić, które metody HTTP (jak GET, POST itd.) są dozwolone przy dostępie. Na przykład jeśli chcesz zezwolić na dostęp tylko przez wysłanie formularza, ustaw:
class AdminPresenter extends Nette\Application\UI\Presenter
{
#[Requires(methods: 'POST')]
public function actionDelete(int $id): void
{
}
}
Dlaczego do akcji zmieniających stan używać POST zamiast GET i jak to robić? Przeczytaj poradnik.
Możesz podać jedną metodę albo tablicę metod. Szczególnym przypadkiem jest wartość '*', która zezwala na
wszystkie metody, czego presentery ze
względów bezpieczeństwa domyślnie nie dopuszczają.
Wywołania AJAX
Jeśli chcesz, żeby presenter albo metoda były dostępne tylko dla żądań AJAX-owych, użyj:
#[Requires(ajax: true)]
class AjaxPresenter extends Nette\Application\UI\Presenter
{
}
To samo pochodzenie
Dla zwiększenia bezpieczeństwa możesz wymagać, żeby żądanie pochodziło z tej samej domeny. Zapobiega to podatności CSRF:
#[Requires(sameOrigin: true)]
class SecurePresenter extends Nette\Application\UI\Presenter
{
}
Dla metod handle<Signal>() dostęp z tej samej domeny jest wymagany automatycznie. Jeśli więc chcesz
zezwolić na dostęp z dowolnej domeny, podaj:
#[Requires(sameOrigin: false)]
public function handleList(): void
{
}
Dostęp przez forward
Czasem przydaje się ograniczyć dostęp do presentera tak, żeby był dostępny wyłącznie pośrednio, na przykład metodami
forward() albo switch() z innego presentera. W ten sposób chronione są na przykład error-presentery,
żeby nie dało się ich wywołać z URL:
#[Requires(forward: true)]
class ForwardedPresenter extends Nette\Application\UI\Presenter
{
}
W praktyce często trzeba oznaczyć pewne widoki, do których można dotrzeć tylko na podstawie logiki w presenterze. Znowu po to, żeby nie dało się ich otworzyć bezpośrednio:
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
{
}
}
Konkretne akcje
Możesz też ograniczyć pewien kod, na przykład tworzenie komponentu, tak żeby był dostępny wyłącznie dla konkretnych akcji w presenterze:
class EditDeletePresenter extends Nette\Application\UI\Presenter
{
#[Requires(actions: ['add', 'edit'])]
public function createComponentPostForm()
{
}
}
W przypadku jednej akcji nie trzeba pisać tablicy: #[Requires(actions: 'default')]
Własne atrybuty
Jeśli chcesz używać atrybutu #[Requires] wielokrotnie z tymi samymi ustawieniami, możesz utworzyć własny
atrybut, który dziedziczy po #[Requires] i konfiguruje go zgodnie z Twoimi potrzebami.
Na przykład #[SingleAction] zezwala na dostęp tylko przez akcję default:
#[\Attribute]
class SingleAction extends Nette\Application\Attributes\Requires
{
public function __construct()
{
parent::__construct(actions: 'default');
}
}
#[SingleAction]
class SingleActionPresenter extends Nette\Application\UI\Presenter
{
}
Albo #[RestMethods] zezwoli na dostęp wszystkimi metodami HTTP używanymi w 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
{
}
Podsumowanie
Atrybut #[Requires] daje Ci dużą elastyczność i kontrolę nad tym, w jaki sposób udostępniane są Twoje
strony. Za pomocą prostych, a zarazem potężnych reguł możesz zwiększyć bezpieczeństwo i poprawne działanie swojej
aplikacji. Jak widzisz, używanie atrybutów w Nette może nie tylko uprościć Twoją pracę, ale też ją zabezpieczyć.