User's identity strikes back

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

Ahoj,

nevím jestli jsem sám koho ta identita štve. A proto přináším návrh na její odstranění.

Celá věc, pokud vím, spočívá ve třech bodech:

  1. Vrácení objektu IIdentity v případě úspěšné autentizace
  2. IIdentity->getRoles() a IIdentity->getName()
  3. Obracení se na Environment::getUser()->identity->whatever pro získání relevantních dat uživatele

Nevím jestli někdo používá vestavěnou třídu Identity – já používám vlastní modelovou třídu.

Navrhuji:

Možnost nastavit službu IUser na vlastní třídu. Což teď stejně dělám – jen s třídou Identity.

Rozhraní IIdentity může být stejně dobře definované pro třídu User.

V případě úspěšné autentizace není problém vrátit TRUE/FALSE, resp. vyhodit vyjímku (to se asi teď děje)

$user->address je prostě lepší než $user->identity->address

Zkrátka a dobře: Přijde mi, že „user je user“ a identity je nadbytečná. Uživatel má identitu vždy ačkoli někdy může být pouze mlhavá (IP, Browser). Předchozí větu berte, prosím, s rezervou ;)

Případně něco jako: $user->isAnonymous() – již nyní existuje $user->isAuthenticated()

Co myslíte?

Editoval pekelnik (23. 1. 2010 12:32)

Honza Marek
Člen | 1664
+
0
-

wtf? Neumim používat identity a proto ji chci zrušit? :)

pekelnik
Člen | 462
+
0
-

Honza Marek napsal(a):

wtf? Neumim používat identity a proto ji chci zrušit? :)

WTF není na místě ;) – Požívat identitu samozřejmě umím. Možná jsem to s tím rušením přehnal, ale spíš jsem jen špatně argumentoval.

Chtěl jsem nastínit něco jiného – User v současně podobě je spíš Session (Nechme teď stranou samotnou třídu Session)

  • $session->isAnonymous();
  • $session->isAuthenticated();
  • $session->setExpiration();

Jakmile startujeme session máme Usera a podobně – bez Session není Usera.

O uživateli chci vědět jiné věci, třeba jeho jméno, adresu, a další – tedy to co je teď v Identity.

Zkrátka bych trojici Session, User a Identity „nacpal“ jenom do Session a User.
S tím že User by byl konfigurovatelný jako služba – tedy možnost použít libovolnou třídu. Například by se mohlo kontrolovat, že je to potomek Nette\Web\User.

Mě nevadí ani Identity ani User tedy krom toho, že třída User má dost často používaný název – tento problém však se jmennými prostory přestává býti problémem.

Jde mi spíš o to že při používání (ano často!) je prostě opruz pořád psát $user->identity->id $user->identity->tasks…

hu?

_Martin_
Generous Backer | 679
+
0
-

S tím si dovolím nesouhlasit. V práci vyvíjíme appku, do které se mohou přihlašovat jak zaměstnanci, tak registrovaní lidé z venku. Všichni mají účty na jednom místě, ale každý už má jinou identitu, která se liší i parametry (návštěvník třeba nemá atribut Oddělení).

pekelnik
Člen | 462
+
0
-

_Martin_ napsal(a):

S tím si dovolím nesouhlasit. V práci vyvíjíme appku, do které se mohou přihlašovat jak zaměstnanci, tak registrovaní lidé z venku. Všichni mají účty na jednom místě, ale každý už má jinou identitu, která se liší i parametry (návštěvník třeba nemá atribut Oddělení).

No jasně – s tím se nedá než souhlasit :)

Můžu se teda zeptat kam po přihlášení nahrajete data o účtu a kam data o identitě?

Je fuk jestli jsou data o uživateli uložená ve třech nebo pěti tabulkách

…protože uživatel by mohl být instancí libovolné třídy.

Při autentizaci by se prostě rozhodlo o tom jaké třídy by měl uživatel být. Defaultně Nette\Web\AnonymousUser. Čili: autentizovala by se session ne uživatel

  • Autentizovaná session obsahuje libovolného uživatele
  • Anonymní session uživatele obsahuje AnonymousUser

Připomíná vám to něco?

Je to to přesně co teď jenom se „zrušenou“ identitou. Opravdu zůstává zachován veškerý současný komfort jako:

<?php
$user->isAuthenticated()
$user->isAllowed(...)
// etc..
?>

Největší výhoda je zjednodušení zápisu:

<?php
$user->identity->name
// vs.
$user->name;
?>

Řekni sám co je lepší :)


Jinak samozřejmě chápu že by to byl totální BC break – chápu mementální situaci tak, že pro takovéhle věci je nejvyšší čas. Předpokládám totiž že po vydání 1.0 se nic takového dlouho dělat nebude.

Ještě mě napadla jedna výhoda a tou by byla snazší a transparentnější konfigurace expirace.

Poznámka:

Kdesi zmiňovaná vhodnost zachování např. oslovení a jiných ne-nebezpečných informací vypršeného uživatele v nevypršené session se dá řešit například přes cookies nebo různými „levely“ přihlášení.

Již nyní je IAuthorizator i IAuthenticator konfigurovatelný jako služba a mohou tedy snadno vracet jaké třídy chtějí.

Editoval pekelnik (23. 1. 2010 21:55)

Aurielle
Člen | 1281
+
0
-

Ten jednodušší zápis se mi líbí :)
(vlastní implementace Identity nepoužívám)

pekelnik
Člen | 462
+
0
-

gmvasek napsal(a):

Ten jednodušší zápis se mi líbí :)
(vlastní implementace Identity nepoužívám)

ha! :)

Honza Marek
Člen | 1664
+
0
-

pekelnik napsal(a):
Možnost nastavit službu IUser na vlastní třídu.

To nejde?

pekelnik
Člen | 462
+
0
-

Honza Marek napsal(a):

pekelnik napsal(a):
Možnost nastavit službu IUser na vlastní třídu.

To nejde?

Tobě jo?

service.Nette-Web-IUser = BlumBlum

tohle v konfigu by mělo způsobit vyjímku při prvním pokusu o získání uživatele.

Editoval pekelnik (23. 1. 2010 22:13)

pekelnik
Člen | 462
+
0
-

ha!
https://forum.nette.org/…pusob-zapisu?…
Citace z uvedeného linku:
>> Hojte, jestli to dávám do špatné sekce tak se omlouvám… už poněkolikáté sem narazil na takovýhle způsob zápisu:

<?php
$this->template->user = $user->isAuthenticated() ? $user->getIdentity() : NULL;
?>

zbytek dotazu se točí kolem neznalostni ternárního operátoru což je ale nepodstatné: uvedený způsob zápisu je totiž skutečně neintuitivní

<?php
$this->template->user = $user;
?>

mi přijde přehlednější…

pekelnik
Člen | 462
+
0
-

Tak bysme si to shrnuli: (Některé detaily jsem (do)myslel trochu jinak) – podstata je však stále stejná :)

Současný User je něco mezi Session a AnonymousUser. Zatímco „skutečný uživatel“ je zabarikádovaný v Identity

Používání identity je neintuitivní – neboť abstrahuje uživatelovu identitu od samotného uživatele – jako kdyby uživatel mohl mít nějaké další identity. Uživatel má zkrátka identitu jednu a basta!

Ano, můžou existovat různé druhy identit. Některé spolu dokonce mohou mít pramálo společného. Například Překadatel má přístup do backendu – do překladů – na nějaký jazyk a nic víc,
naproti tomu třeba Zákazník může nakupovat, platit, má X adres, společností etc. Zaměstanec má kód od alarmu, mobil, pracovní dobu, úkoly etc etc.. Tento příklad zmiňoval _Martin_.

Zkrátka a dobře: Uživatelé mohou být uloženi třeba na měsíci. To je záležitost autentizačního handleru. Ten zodpovídá za to, kdo se přihlásí, a také ví kdo to je, kopodivu, že? Instance jeho identity se nyní vrací v případě úspěšné autentizace.

Já navrhuji toto:

  1. Autentizační handler v případě úspěšné autentizace vrátí objekt Uživatele
  2. Uživatel musí implementovat rozhraní IUser
  3. Session obsahuje Uživatele vždy (pokud běží)
  4. V případě, že se jedná o anonymní session, existuje Uživatel stále jako instance třídy Nette\Web\(Anonymous)User
  5. V případě, že se jedná o přihlášeného uživatele, může tento být instancí libovolné třídy.

Poznámky na závěr:

Třída User obsahuje několik metod, které by patrně bylo třeba přesunout do Session, např.: setAuthenticationHandler, setExpiration … ale to jsou detaily.

Konfigurace jako služba je blbost – to se omlouvám za zmatení. (Uživatel je vždy produkt autentizačních procesů)

Příklad: Uživatel a Identita nechť jednou jsou! …tohle je zmatené:

<?php
public function getRoles()
{
	if (!$this->isAuthenticated()) {
		return array($this->guestRole);
	}

	$identity = $this->getIdentity();
	return $identity ? $identity->getRoles() : array($this->authenticatedRole);
}
?>

Jako AnonymousUser by tady bylo return array($this->guestRole);

KISS

Heslo dne: Není větší rozkoše, ani radosti, ani tělesných hříchů, než centrální větrání.

David Grudl
Nette Core | 8111
+
0
-

Mám pocit, že tohle téma pramení z nedorozumění. Třída Nette\Web\User je tím, co nazýváš Session a třída Identity tím, co nazýváš User. Je fakt, že User by se mohl spíš jmenovat UserManager nebo tak nějak, také Identity spíš UserIdentity, jenže názvy počítaly se jmennými prostory a Web::User bylo o něco srozumitelnější, než samotné User.

Z tohoto pohledu by se třeba nepřihlášený uživatel nejmenoval Nette\Web\AnonymousUser ale Nette\Security\AnonymousIdentity. O tom, jestli něco takového implementovat, se tuším kdysi dávno otevřela diskuse ale bez výsledku. Přidal bych něco jako User::$guestIdentity, které by se vracelo namísto NULL při volání getIdentity().

PetrP
Člen | 587
+
0
-

David Grudl napsal(a):

Přidal bych něco jako User::$guestIdentity, které by se vracelo namísto NULL při volání getIdentity().

Něco takového již používám takže jsem pro začlenění do fw.

pekelnik
Člen | 462
+
0
-

David Grudl napsal(a):

Mám pocit, že tohle téma pramení z nedorozumění.

No nejedna se ani tak o nedorozumeni – ja tomu rozumim – jako spise o nespokojenost s pojmenovanim a zpusobem pouziti.

Třída Nette\Web\User je tím, co nazýváš Session a třída Identity tím, co nazýváš User. Je fakt, že User by se mohl spíš jmenovat UserManager nebo tak nějak, také Identity spíš UserIdentity, jenže názvy počítaly se jmennými prostory a Web::User bylo o něco srozumitelnější, než samotné User.

Souhlasim a proti tomuhle jsem prave brblal :)

Z tohoto pohledu by se třeba nepřihlášený uživatel nejmenoval Nette\Web\AnonymousUser ale Nette\Security\AnonymousIdentity. O tom, jestli něco takového implementovat, se tuším kdysi dávno otevřela diskuse ale bez výsledku. Přidal bych něco jako User::$guestIdentity, které by se vracelo namísto NULL při volání getIdentity().

Zminena guestIdentity problemy defakto resi.

Byl bych pro nejaky mechanismus podobny soucasnemu User::getId() pro ziskavani informaci z identity.

paranoiq
Člen | 392
+
0
-

pokud bude User::getIdentity() vracet místo NULL nějaký NULL-object, tak by možná k přehlednějšímu kódu pomohla zkratka Environment::getIdentity() nebo Environment::getUserIdentity() – místo současného Environment::getUser()->getIdentity()

rokerkony
Člen | 122
+
0
-

Environment::getUserIdentity() +1

veena
Člen | 98
+
0
-

Ještě malá poznámka. Nehodilo by se Usera nebo Identitu (opravdu v tom nemám tolik jasno ;) oddělit aby nezávisely jen na Session? Co když chci identifikovat „anonymní“ usery, kteří navštěvují moje stránky a nějak s nimi pracovat? Třeba je nechat hlasovat v anketě jen jednou. Pak bych asi chtěl používat na identifikaci spíš cookie s dlouhou dobou expirace než session. Nebo kdybych chtěl logovat jaké stránky uživatelé prochází za sebou a kolik na nich tráví času. Mít čítač přečtení článku apod. To jsou všechno use cases spíš na cookiny než na sessiony, ne? Případně může být použita jindy i jiná identifikace např. string v url, IP adresa, HTTP hlavička apod.

Prostě tak jako má session možnost více úložišť (db, file, atd), tak identita by měla mít více možností, na co ji vázat.

Je to tak, nebo sem mimo?

David Grudl
Nette Core | 8111
+
0
-

Narazil jsem při implementaci objektu guestIdentity na drobný BC break při detekci „guest“ identity.

Dejme tomu, že výchozí hodnota bude

$user->guestIdentity = new Identity(NULL, 'guest');

Takto bude možné bezpečně volat $user->identity->id (vrátí NULL) i $user->getRoles() (vrátí ‚guest‘) a vzhledem k implementaci výchozí Identity nebude ani např. {$user->identity->name} způsobovat E_NOTICE.

Nicméně přestane fungovat v kódu docela běžný dotaz {if $user->identity}...{/if}, testující, zda není identita „guest“. Testování přes instanceof by v šablonách bylo hloupé. Takže buď přímo do třídy User přidat metody hasIdentity() nebo isIdentityGuest(), nebo dát guestIdentitě proměnnou isGuest = TRUE. Co myslíte?

Honza Marek
Člen | 1664
+
0
-

David Grudl napsal(a):

Nicméně přestane fungovat v kódu docela běžný dotaz {if $user->identity}...{/if}, testující, zda není identita „guest“.

Otázka, jestli tohle lidi vědí. Mě neexistence identity u guesta nepříjemně překvapila až poměrně nedávno, takže normálně všude testuju {if $user->isInRole("guest").

Navíc dotazovat se na identity, když chci zjistit, jestli je uživate guest, to je takové divné.

Editoval Honza Marek (12. 4. 2010 9:28)

hrach
Člen | 1834
+
0
-

Já si osobně v aktuálním stavu libuji. Dle mě je to naprosto průhledné. Stačí si jen neplést dané pojmy…

pekelnik
Člen | 462
+
0
-

David Grudl napsal(a):

Narazil jsem při implementaci objektu guestIdentity na drobný BC break při detekci „guest“ identity.

Dejme tomu, že výchozí hodnota bude

$user->guestIdentity = new Identity(NULL, 'guest');

Takto bude možné bezpečně volat $user->identity->id (vrátí NULL) i $user->getRoles() (vrátí ‚guest‘) a vzhledem k implementaci výchozí Identity nebude ani např. {$user->identity->name} způsobovat E_NOTICE.

Nicméně přestane fungovat v kódu docela běžný dotaz {if $user->identity}...{/if}, testující, zda není identita „guest“. Testování přes instanceof by v šablonách bylo hloupé. Takže buď přímo do třídy User přidat metody hasIdentity() nebo isIdentityGuest(), nebo dát guestIdentitě proměnnou isGuest = TRUE. Co myslíte?

Šel bych touto cestou:

<?php

$user->isAnonymous(); // jako zkratka na cokoliv... třeba na
$user->identity->isAnonymous === TRUE // isGuest() - není tolik výstižné
// nebo
$user->identity->roles === array('guest') // to už je fuk...

$user->hasIdentity() === ! $user->isAnonymous()
?>
David Grudl
Nette Core | 8111
+
0
-

hrach napsal(a):

Já si osobně v aktuálním stavu libuji. Dle mě je to naprosto průhledné. Stačí si jen neplést dané pojmy…

No já taky, ale evidentně jsme sami ;)

pekelnik napsal(a):
$user->isAnonymous(); // jako zkratka na cokoliv… třeba na

Ano, anonymous je rozhodně lepší, guest totiž vytváří dojem, že uživatel není přihlášen. Dobrá připomínka.

Cifro
Člen | 245
+
0
-

Len pripomínam toto vlákno. Objaví sa niečo z toho v 1.0?

David Grudl
Nette Core | 8111
+
0
-

Nevím…