Timeout a ošetření nezpracovaných dat

Framework sleduje u každého přihlášeného uživatele čas uplynulý od jeho posledního dotazu na server. Překročí-li určitou hodnotu, je uživatel automaticky odhlášen. Jak již bylo zmíněno v obecné kapitole Timeout a ošetření nezpracovaných dat, může v důsledku toho dojít při delší prodlevě mezi dvěma uživatelovými dotazy ke ztrátě odeslaných formulářových dat. V takovém případě by bylo záhodno tato data uchovat, nabídnout uživateli znovupřihlášení a bezprostředně po něm data zpracovat.

V systému phpBASE se zaslaná data požadavku automaticky ukládají vždy, když je vynuceno přihlášení uživatele. Tedy nejen bezprostředně po timeoutu, ale obecně při jakémkoliv pokusu nepřihlášeného uživatele otevřít stránku či skript, ke kterým jako anonym nemá přístup. Pro zajištění jejich následného obnovení stačí programátorovi provést jen dvě drobné úpravy v jeho dosavadní aplikaci. Jednak přidat funkci Form_Restore do běžného přihlašovacího formuláře a jednak přidat funkci Restore do skriptu zpracovávající data z tohoto formuláře odeslaná.

V následujících odstavcích si ukážeme, jak celý mechanizmus uchování a následného obnovení požadavku funguje uvnitř systému.

Uchování nezpracovaných dat

Nepřihlášený uživatel zašle serveru požadavek na nějakou stránku či skript. Kvůli nedostatečným právům mu ale není přístup povolen.

Systém nejprve vygeneruje náhodný jedinečný identifikátor požadavku. Každému nevyřízenému požadavku se přiřazuje nový identifikátor, pod kterým je evidován. Díky tomu může být po úspěšném přihlášení zpracováno i více vzniklých požadavků, pokud uživatel pracuje v rámci jedné session s více otevřenými okny prohlížeče. Identifikátor se uloží do globální proměnné $NEXT.

Nezpracovaná data se uchovávají v globální session proměnné $RESTORE_DATA. Jedná se o vícerozměrné pole, kde první dimenzí jsou právě jednotlivé identifikátory požadavků. Pod nimi se ukládají hodnoty těchto proměnných:

Žádné další proměnné (jmenovitě zejména $_SESSION, $_COOKIES, $_SERVER, $_ENV, $GLOBALS) není potřeba uchovávat. Při budoucím obnovení dat totiž budou beztak k dispozici. A to buď v nezměněné podobě, nebo s novými hodnotami podle aktuálního stavu aplikace, session a serveru.

Přihlašovací stránka

Poté, co jsou data požadavku takto uložena, se uživateli pošle standardní přihlašovací stránka. Do login formuláře je ovšem nutné navíc přidat skryté pole, kterým se dál předá právě identifikátor našeho nezpracovaného požadavku. K tomu slouží funkce Form_Restore. Celý formulář bude (samozřejmě ve zjednodušené podobě) vypadat například takto:

<?php
Form_Start
(); ?>

    Login: <input type="text" name="login" />
    Heslo: <input type="password" name="heslo" />

    <input type="submit" value="Přihlásit se" /><?php

    Form_Restore
();
    
Form_Script();
Form_End();
?>

Předpokládejme, že uvedený kód je součástí login stránky user/login. Pak je klientovi zaslán výsledný HTML kód:

<form action="index.php" method="post">

    Login: <input type="text" name="login" />
    Heslo: <input type="password" name="heslo" />

    <input type="submit" value="Přihlásit se" />

    <input type="hidden" name="NEXT" value="ba1ec1ade093fe681a0e6073" />
    <input type="hidden" name="SCRIPT" value="user/login" />

</form>

Zpracování přihlašovacích údajů

Data odeslaná z tohoto formuláře budou zpracována skriptem user/login. Ten je nutné modifikovat v tom smyslu, aby po úspěšném přihlášení obnovil a provedl náš původní nezpracovaný požadavek. K tomu slouží funkce Restore:

<?php
$login 
$_POST["login"];
$password $_POST["password"];

if (
Auth_Login($loginMD5($password))) {
    
Restore();
    
Redirect("/main/index");

} else {
    
Direct();
}
?>

Kdyby se přihlášení nepodařilo, zobrazila by se znovu login stránka. Ale už se samozřejmě negeneruje nový identifikátor nezpracovaného požadavku, funkce Form_Restore v ní vloží do skrytého pole stejný identifikátor jako předtím.

Předpokládejme však, že přihlášení proběhlo úspěšně. Nejprve se volá funkce Restore. Ta zkontroluje, jestli byl společně s login formulářem odeslán i identifikátor nezpracovaného požadavku $NEXT. Pokud nebyl, znamená to, že zobrazení přihlašovací login stránky nebylo vynucené. Musíme si uvědomit, že se na ni uživatel mohl dostat i „dobrovolně“ prostě tak, že klikl na nějaký odkaz na login stránku přímo směřující. Pak běh skriptu pokračuje dál k funkci Redirect, která provede přesměrování na zadanou stránku main/index.

My však uvažujeme vynucený login. To znamená, že mezi formulářovými daty odeslanými z přihlašovací stránky se tedy nachází i identifikátor nezpracovaného požadavku $NEXT. V takovém případě funkce Restore provede přesměrování na speciální pseudoskript restore, a to včetně parametru $NEXT. V našem případě tedy klienta přesměruje na http://www.example.com/index.php?SCRIPT=restore&NEXT=ba1ec1ade093fe681a0e6073.

O pseudoskriptu mluvíme v případě SCRIPT=restore proto, že se nejedná o klasický identifikátor skriptu. Chybí mu totiž určení modulu, které je normálně povinné. Proto mu neodpovídá žádný reálný soubor, nemůže tedy dojít ani k žádné případné kolizi se skutečným skriptem. Pro systém má však zvláštní význam.

Obnovení uchovaných dat a provedení původního dotazu

Když Controller při zpracování klientského požadavku rozliší mezi zaslanými parametry pseudoskript SCRIPT=restore, znamená to pro něj, že má podle doprovodného parametru $NEXT obnovit a provést uchovaný dotaz. Ze session proměnné $RESTORE_DATA tedy vyjme příslušná data a přiřadí je zpět do superglobálních proměnných $_GET, $_POST a $_REQUEST. Jestliže je v konfiguraci PHP nastavena direktiva register_globals=on, pak také v souladu se tím přiřadí obnovované hodnoty do příslušných globálních proměnných.

Dále pokračuje běh Controlleru běžným způsobem. Jelikož byly obnoveny všechny původně zaslané parametry, dojde tím nyní k jejich řádnému zpracování.

Poznámky k ošetření nezpracovaných dat

Co se týká bezpečnosti tohoto mechanizmu, musíme si uvědomit, že uchovaná data jsou vázána na aktuální session. Lze je vyvolat právě jen dotazem v rámci této session a s uzavřením session se definitivně ztrácí i tato data. Riziko jejich zneužití či pozdějšího opětovného zavolání tedy přímo souvisí pouze s rizikem převzetí session jako takové.

Popsaným způsobem není bohužel možné uchovávat soubory zaslané společně s požadavkem. Umožnění uchování a obnovy zaslaných souborů by totiž přineslo velká bezpečnostní rizika a konfigurační problémy. Neboli ztrácí se data původně dostupná například přes superglobální proměnnou $_FILES. S tím by měl autor programující nad phpBASE nějakým způsobem počítat.

Jestliže se obnovovaný požadavek skládal pouze z parametrů typu GET, pak z nich funkce Restore zpětně poskládá původní URL a klienta přesměruje přímo na ni. Vynechá se tak složitější postup přes pseudoskript SCRIPT=restore, který se tedy použije jen na obnovení dat zaslaných metodou POST.