Chystá se nette/forms 3.3, prosím o otestování
- David Grudl
- founder | 8315
Poprosil bych o testování nette/forms 3.3.0-RC, která přináší modernizaci CSRF ochrany, zpřehlednění Latte integrace a několik změn zpětné kompatibility.
Jako vždy, cílem rozhodně není komukoliv rozbít kód. Některé změny ale nutně zasahují do chování, a proto sháním zpětnou vazbu. Pokud vám něco přestane fungovat nebo narazíte na nečekané deprecated hlášky, dejte prosím vědět.
Vyžaduje PHP 8.3 nebo vyšší.
Bezpečnost: nová CSRF ochrana
Formuláře se nově brání proti CSRF kontrolou hlavičky
Sec-Fetch-Site (striktní same-origin) namísto dosavadní SameSite
cookie. Tyto hlavičky přidává prohlížeč sám a nelze je zfalšovat ani
při XSS zranitelnosti, takže ochrana nepotřebuje token ani stav na serveru.
Podrobně to popisuje článek CSRF konečně
řeší prohlížeč.
Co to znamená v praxi:
- Token-based ochrana (
$form->addProtection()) je označena jako deprecated. Stále funguje, takže není nutné nic urychleně přepisovat, ale výchozí ochrana ji plně nahrazuje. - Kontrola je nově striktně same-origin. Dosavadní mechanismus (SameSite cookie) považoval za bezpečné i odeslání mezi subdoménami téhož webu; nová kontrola vyžaduje přesnou shodu originu (scheme + host + port).
Pokud formulář legitimně přijímáte z jiného originu
(cizí web nebo jiná subdoména), lze vestavěnou kontrolu vypnout pomocí
$form->allowCrossOrigin(). Ale pozor, tím se CSRF ochrana vypne
úplně a formulář přijme požadavek odkudkoliv, takže to
dělejte s rozmyslem. Chcete-li povolit jen konkrétní originy, kontrolu
vypněte a původ si ověřte ručně proti vlastnímu seznamu pomocí hlavičky
Origin:
$form->allowCrossOrigin(); // vypne vestavěnou kontrolu
$allowed = ['https://app.example.com', 'https://admin.example.com'];
$form->onValidate[] = function (Form $form) use ($httpRequest, $allowed) {
if (
!$httpRequest->isFrom(Nette\Http\FetchSite::SameOrigin)
&& !in_array((string) $httpRequest->getOrigin(), $allowed, strict: true)
) {
$form->addError('Neplatný původ požadavku.');
}
};
$httpRequest získáte v presenteru přes
$this->getHttpRequest(). getOrigin() vrátí
null, pokud hlavička chybí, takže neznámý původ se
odmítne.
Latte: sjednocení {formContext} a
{formContainer}
Doteď existovaly dvě samostatné značky pro práci s formulářem bez
vykreslení <form>: {formContext} zakládal
kontext celého formuláře (vzatého z presenteru, když je samotný
<form> vykreslen jinde), kdežto {formContainer}
vstupoval do vnořeného containeru už otevřeného formuláře. Rozdíl byl
jemný a pro řadu lidí matoucí – nebylo zřejmé, kdy sáhnout
po které.
Nově je obojí zastřešuje jediná konstrukce
{form scope name}:
- klíčové slovo
scopeřekne{form}, aby nevypisoval HTML značku<form>, jen vložil formulář/container na zásobník (takže se k němu vážou{input},{label},{inputError}), - pokud už je nějaký formulář aktivní, název se vyhodnotí
relativně vůči němu (chování
{formContainer}), jinak z kořenové úrovně (chování{formContext}).
Jeden zápis tak pokryje oba dosavadní případy. Původní
{formContext} i {formContainer} přitom zůstávají
funkční, ale časem budou deprecated ve prospěch
{form scope}.
Latte: {form detached name}
Umožnuje vytvářet vnořené formuláře! HTML zakazuje vnořené značky
<form> a prohlížeč vnitřní <form>
zahodí a jeho inputy „přilepí“ k vnějšímu formuláři. Klíčové
slovo detached to řeší: vypíše prázdný
<form></form> na začátku a každý prvek na něj
naváže přes HTML atribut form="...". Prvky se tak odešlou do
správného formuláře bez ohledu na to, kde v DOM leží.
Pokud máte dva vnořené formuláře, vypište jako
{form detached} ten vnější (který ten druhý
obklopuje). Jeho prázdná značka <form> se uzavře hned na
začátku, takže vnitřní formulář už nebude vnořený a oba budou fungovat
správně. A nebo jako detached vypište oba.
Latte: požadavky a interní změny
- Chybějící čárka před argumenty
{form}je nově deprecated – místo{form name attr=val}pište{form name, attr=val}. Důvodem je právě možnost psát za názvem klíčová slovascope/detached. - Odstraněna podpora Latte 2 i Latte 3.0 – nově je potřeba Latte 3.1.4+.
- Vnitřní
Runtimebyl přepsán ze statické na běžnou (nestatickou) třídu. Je to interní změna, která se vás dotkne jen v případě, že jste statické metodyRuntimevolali napřímo z vlastního kódu.
Novinka: callback u addSubmit()
Tlačítku lze nově předat handler rovnou při vytvoření, bez
samostatného ->onClick[] =:
$form->addSubmit('send', 'Odeslat', function (SubmitButton $button, $values) {
// ...
});
Změny zpětné kompatibility
setValues() / setDefaults() – příprava
na zúžení přijímaných typů. Podporovaným vstupem je nově
iterable|stdClass (pole, Traversable,
stdClass). Funkčně se zatím nic nemění, ale předáte-li jiný
typ objektu – typicky entitu nebo DTO, které se dosud převáděly na pole
přes (array) (což u privátních a chráněných vlastností
dává „pokroucené" klíče) – dostanete deprecated hlášku. V takovém
případě si data převeďte na pole sami.
Další změny chování:
RadioList/CheckboxList:getControlPart()agetLabelPart()vyhodí výjimku při neexistujícím klíči (dříve místo toho vznikl nesmyslný výstup).- Negativní validační pravidla (
~Rule) nově vyhodí výjimku (dříve jen deprecated hláška).
Odstraněno (dříve deprecated):
DataClassGenerator, LatteRenderer,
getValues(true) (použijte getValues('array')),
parametr $default v getOption() (použijte operátor
??).
Nově deprecated (stále funguje, jen hlásí
upozornění) – některé magické property. Místo
$control->htmlName, caption, omitted,
filled, options, selectedItem(s),
submittedBy a $form->action, method,
renderer používejte odpovídající metody
(getHtmlName(), getCaption(),
isOmitted(), isFilled(), getOptions(),
getSelectedItem(), isSubmittedBy(),
getAction(), getMethod(),
getRenderer()).
Děkuji za pomoc s testováním!