User's identity strikes back
- pekelnik
- Člen | 462
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:
- Vrácení objektu IIdentity v případě úspěšné autentizace
- IIdentity->getRoles() a IIdentity->getName()
- 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)
- pekelnik
- Člen | 462
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
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
_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)
- Honza Marek
- Člen | 1664
pekelnik napsal(a):
Možnost nastavit službu IUser na vlastní třídu.
To nejde?
- pekelnik
- Člen | 462
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
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:
- Autentizační handler v případě úspěšné autentizace vrátí objekt Uživatele
- Uživatel musí implementovat rozhraní IUser
- Session obsahuje Uživatele vždy (pokud běží)
- V případě, že se jedná o anonymní session, existuje Uživatel stále jako instance třídy Nette\Web\(Anonymous)User
- 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 | 8218
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().
- pekelnik
- Člen | 462
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řídaIdentity
tím, co nazývášUser
. Je fakt, žeUser
by se mohl spíš jmenovatUserManager
nebo tak nějak, takéIdentity
spíšUserIdentity
, jenže názvy počítaly se jmennými prostory aWeb::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
aleNette\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.
- veena
- Člen | 98
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 | 8218
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
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)
- pekelnik
- Člen | 462
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ěnnouisGuest = 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 | 8218
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.