Uprava udajov uzivatela v metode authenticate

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
sabrx
Člen | 47
+
0
-

Zdravim, mam takyto problem s metodou authenticate v modeli Authenticator na prihlasovanie.

Mam tabulku st_user obsahujucu stlpec tz_city_id, co je cudzi kluc do tabulky tz_city s nazvami miest. V aplikacii potrebujem pracovat s nazvom mesta, a tak pri prihlasovani uzivatela vykonam v metode authenticate:

<?php
if ($row)
	$row->tz_city_id = $row->tz_city->city_name;
?>

cim prepisem id mesta, ktore sa vytiahlo zo zaznamu uzivatela, jeho nazvom z tabulky miest.

A teraz k problemu. To prepisovanie funguje chvilu (zopar prihlaseni), a potom to zrazu pri dalsom prihlaseni prestane fungovat, udaj sa vobec neprepise, ako by tam ten riadok kodu ani nebol. Dakujem za pomoc. Mam Nette 2.0.5 na localhost, PHP 5.4.6.

vvoody
Člen | 910
+
0
-

Ukáž nám celý authenticator + kód ako pristupuješ k dátam identity.

sabrx
Člen | 47
+
0
-

Na prihlasovanie samozrejme pouzivam metodu login. Ide mi o to zistit, ako je to medzi sebou prepojene, kedze raz sa ten kod vykona, inokedy zas nie. Reflection je urcite v poriadku, lebo by to inak vyhadzovalo „no reference found“.

sabrx
Člen | 47
+
0
-

Models\Authenticator.php:

<?php
	/**
	 * Performs an authentication
	 * @param  array
	 * @return Nette\Security\Identity
	 * @throws Nette\Security\AuthenticationException
	 */
	public function authenticate(array $credentials)
	{
		list($username, $password) = $credentials;
		$row = $this->users->findByName($username);

		if ($row)
			$row->tz_city_id = $row->tz_city->city_code;

		if (!$row) {
			throw new NS\AuthenticationException("User '$username' not found.", self::IDENTITY_NOT_FOUND);
		}

	    if ($row->pass_hash !== self::calculateHash($password, $row->pass_hash)) {
	        throw new NS\AuthenticationException("Invalid password.", self::INVALID_CREDENTIAL);
	    }

	    if (!$row->activated) {
			throw new NS\AuthenticationException("Account not yet activated.", self::NOT_APPROVED);
		}

	    unset($row->pass_hash);
	    return new NS\Identity($row->id, NULL, $row->toArray());
	}
?>

V sablone si zavolam:

<?php
{$user->getIdentity()->tz_city_id}
?>
sabrx
Člen | 47
+
0
-

V Authenticator.php je este samozrejme toto:

<?php
	/** @var UserTable */
	private $users;

	public function __construct(UserTable $users)
	{
		$this->users = $users;
	}
?>
sabrx
Člen | 47
+
0
-

Este dodam, ze city_codetz_city je skutocne retazec (nazov casovej zony), keby si niekto nahodou myslel, ze tam mam to iste ako v ID :-)

elendir
Člen | 31
+
0
-

potom to zrazu pri dalsom prihlaseni prestane fungovat

Přestane to fungovat (resp. identita je špatně) hned po zavolání login() nebo až po přesměrování?

Editoval elendir (29. 10. 2012 20:15)

sabrx
Člen | 47
+
0
-

Az po presmerovani, kedze tz_city_id volam az v sablone. Volam tam tento kod:

<?php
date_default_timezone_set($user->getIdentity()->tz_city_id)
?>

Ak sa to nastavi, je tam napriklad „Europe\Prague“, takze OK, inak to vyhodi chybu, co je jasne, lebo ked sa to neprepise v authenticate, je tam ID.

elendir
Člen | 31
+
0
-

Identita se instancializuje dvěma způsoby – buď uvnitř login() anebo ze $_SESSION (pro dříve zalogovaného uživatele). Na tvém místě bych nejdřív zkusil zjistit, kde se ta identita instancializuje špatně – jestli je to hned v login():

$user->login(...);
Nette\Diagnostics\Debugger::dump($user->getIdentity());

anebo až po přesměrování, poté co je zrekonstruována ze $_SESSION.

Také bych zkusil to pole hodnot do identity neposílat přes

$row = $this->users->findByName($username);
$row->tz_city_id = $row->tz_city->city_code;
return new NS\Identity($row->id, NULL, $row->toArray());

protože kdoví jaká magie se uvnitř těchto tříd skrývá. Nevím, nikdy jsem s nimi nepracoval, ale abych vyloučil ukládání jakýchkoliv neserializovatelných objektů a referencí do identity, změnil bych to alespoň dočasně na:

$row = $this->users->findByName($username);
$data = array();
$data['tz_city_id'] = $row->tz_city->city_code;
return new NS\Identity($row->id, NULL, $data);

Je samozřejmě možné že to ani potom nebude fungovat, ale minimálně se tím razantně zúží prostor kde hledat problém.

Editoval elendir (29. 10. 2012 21:10)

sabrx
Člen | 47
+
0
-

Vdaka za dobre rady, zatial to funguje, takze budem musiet pockat, az sa to zase pokazi.

Co sa tyka metody findByName, tam nie je ziadna magia, je to v modeli UserTable, a vracia zaznam vo forme pola:

<?php
	public function findByName($username) {
		return $this->findBy(array('username' => $username))->fetch();
	}
?>

Metoda findBy vracia ActiveRow. To mi vlastne doslo az teraz, nemohlo by to byt tym, ze sa snazim priamo v objekte ActiveRow nastavit hodnotu atributu?

elendir
Člen | 31
+
0
-

nemohlo by to byt tym, ze sa snazim priamo v objekte ActiveRow nastavit hodnotu atributu?

Vyloučit to nelze, tu třídu neznám, ale to by ti to nefungovalo nikdy, ne? Ty ale píšeš že to nefunguje „jak kdy“. Proto úplně první věc co je imho potřeba zjistit je jestli se správně uložila ta identita. Píšeš že už ti to funguje, no kdyby přestalo, přidej si hned za

$user->login(...);

(což předpokládám máš v Sign:in) tyto řádky:

Nette\Diagnostics\Debugger::$maxDepth = 10;
Nette\Diagnostics\Debugger::dump($user->getIdentity());
Nette\Diagnostics\Debugger::dump($_SESSION);

aby se zjistilo jestli se správně vytvořila identita a jestli se zároveň správně uložila do session. Pak sem postni oba ty dumpy. Dokud neuděláš tohle tak těžko vyvozovat nějaké závěry. Taky se zkus mrknout na vlákno přepsání identity, jestli by s tím náhodou tvůj problém nemohl souviset.

sabrx
Člen | 47
+
0
-

Takže práve som náhodou zistil, kedy to prestáva fungovať. Keď sa pokúsim prihlásiť s nesprávnym heslom, a následne správnym, tz_city_id vo vraátenej identite je nezmenené, teda zmena

<?php
if ($row)
            $row->tz_city_id = $row->tz_city->city_code;
?>

vykonaná v metóde authenticate (pozri môj prispévok vyššie) sa neprejaví. Po premazaní cache sa zmena už prejaví, a identita obsahuje upravenú premennú.

Moja domnienka je, ze sa pri prvom pokuse o prihlásenie uloží pôvodný záznam z tabuľky do cache, a pri druhom pokuse sa odtiaľto vytiahne, lebo Nette predpokladá, ze v authenticate nebudem nič modifikovať, takže na čo zasa zaťažovať databázu rovnakým dotazom. Je na tom niečo? Ak áno, dá sa tomuto správaniu nejako zabrániť?

sabrx
Člen | 47
+
0
-

elendir napsal(a):

nemohlo by to byt tym, ze sa snazim priamo v objekte ActiveRow nastavit hodnotu atributu?

Vyloučit to nelze, tu třídu neznám, ale to by ti to nefungovalo nikdy, ne? Ty ale píšeš že to nefunguje „jak kdy“. Proto úplně první věc co je imho potřeba zjistit je jestli se správně uložila ta identita. Píšeš že už ti to funguje, no kdyby přestalo, přidej si hned za

$user->login(...);

(což předpokládám máš v Sign:in) tyto řádky:

Nette\Diagnostics\Debugger::$maxDepth = 10;
Nette\Diagnostics\Debugger::dump($user->getIdentity());
Nette\Diagnostics\Debugger::dump($_SESSION);

aby se zjistilo jestli se správně vytvořila identita a jestli se zároveň správně uložila do session. Pak sem postni oba ty dumpy. Dokud neuděláš tohle tak těžko vyvozovat nějaké závěry. Taky se zkus mrknout na vlákno přepsání identity, jestli by s tím náhodou tvůj problém nemohl souviset.

Po pridani prikazu

<?php
Nette\Diagnostics\Debugger::dump($_SESSION);
?>

sa mi vratila chyba Nette\InvalidStateException, Cannot set HTTP code after HTTP headers have been sent.

sabrx
Člen | 47
+
0
-

Zdravím, takže asi som ten problém už vyriešil. Metódu authenticate som upravil tak, že riadok databázy $row som hneď previedol na pole, a až v tomto poli som si upravil príslušnú položku. Do identity som samozrejme pridal toto pole. Určite tu ide o to, že Nette vždy nazrie do cache, či tam náhodou nie je rovnaký záznam. Aj keby áno, stále to nevysvetľuje, prečo sa kód

<?php
if ($row)
            $row->tz_city_id = $row->tz_city->city_code;
?>

vykoná len vtedy, keď sa do procesu nezapája cache.

Editoval sabrx (9. 11. 2012 12:12)