Nette Documentation Preview

syntax
Jak używać atrybutu `#[Requires]`
*********************************

.[perex]
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`:

```php
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:

```php
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 |post-links].

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ą |application:presenters#Kontrola metody HTTP].


Wywołania AJAX
--------------

Jeśli chcesz, żeby presenter albo metoda były dostępne tylko dla żądań AJAX-owych, użyj:

```php
#[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 |nette:vulnerability-protection#Cross-Site Request Forgery (CSRF)]:

```php
#[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:

```php
#[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:

```php
#[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:

```php
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:

```php
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`:

```php
#[\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:

```php
#[\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ć.

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ć.