Good Practice pro přihlašování/práva

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

Ahoj,

dělám s Nette teprv chvilku a řeším, jak správně řešit přihlašování a práva na určité sekce aplikace. V seriálu na zdrojáku toho moc není.

V examples/CD-Collection už něco je, ale přijde mi to příliš hardcoded (celý BasePresenter je zabezpečen identicky, bez možnosti rolí nebo veřejných sekcí. Řešení by bylo mít PublicBasePresenter < BasePresenter a PrivateBasePresenter < BasePresenter, kde jeden přihlášení vyžaduje a druhý ne. Ostatní presentery by z nich dědily. Ale stále to není příliš variabilní.

Poslední mě napadá psát na začátek každé akce presenteru autorizaci. To je ale obrovská duplikace kódu.

Zajímá mě, jestli to lze vyřešit nějak elegantně. Např. v Djangu se mi líbí struktura:

@login_required
def my_view(request):
...

nebo

@permission_required('polls.can_vote', login_url="/login/")
def vote(request):
...

a můžu ke každé akci daného view (ekvivalent k nette presenteru) nastavovat práva dle libosti.

Jak takový problém řešíte vy?

Ondřej Mirtes
Člen | 1536
+
0
-

Můžeš využít třídu Permission a do ní si nalít data z databáze + kontrolovat oprávnění každé akce ve startupu BasePresenteru :)

romansklenar
Člen | 655
+
0
-

Něco takového není problém zařídit, mrkni se pro inspiraci zde.

Savannah
Člen | 30
+
0
-

Ondřej Mirtes napsal(a):

Můžeš využít třídu Permission a do ní si nalít data z databáze + kontrolovat oprávnění každé akce ve startupu BasePresenteru :)

Jasně, ale šlo mi spíše o to napojení na Presenter. Abych nemusel v každé akce presenteru psát něco jako

public function renderFoo () {
  if ($acl->isAllowed('guest', 'poll', 'vote') == false) {
    $this->redirect('Auth:login', array('backlink' => $backlink));
    return;
  }
  // continue with action
}

romansklenar napsal(a):

Něco takového není problém zařídit, mrkni se pro inspiraci zde.

jo, tohle by mohlo být to, co hledám :) Kde má tohle dokumentaci plz? Abych věděl, jak to tam pak probíhá?

romansklenar
Člen | 655
+
0
-

Nemá to nikde dokumentaci, není to součástí distribuce. Když se mrkneš do BasePresenteru na metodu startup() tak se tam akorát volá kontrola, jestli třída, handler signálu, metoda akce nebo renderer pohledu nemá anotaci @secured, pokud ano a uživatel není autentizován přesměruje na presenter, kde se může přihlásit a po úspěšném přihlášení ho vrátí na původní akci.

Ondřej Mirtes
Člen | 1536
+
0
-

Já mám v BasePresenter::startup kontrolu, zda-li má uživatel povolení pro resource (kterým je název Presenteru) a privilege (kterým je název action či signálu), pokud ne, vyhazuji BadRequestException(‚msg‘, 403) :)

Savannah
Člen | 30
+
0
-

romansklenar napsal(a):

Nemá to nikde dokumentaci, není to součástí distribuce. Když se mrkneš do BasePresenteru na metodu startup() tak se tam akorát volá kontrola, jestli třída, handler signálu, metoda akce nebo renderer pohledu nemá anotaci @secured, pokud ano a uživatel není autentizován přesměruje na presenter, kde se může přihlásit a po úspěšném přihlášení ho vrátí na původní akci.

No v tom, co jsi mi posílal, je jenom

class ArticlePresenter extends BasePresenter {
	protected function startup() {
		$this->model = new ArticlesModel();
		parent::startup();
	}

ve startupu :(

Ondřej Mirtes napsal(a):

Já mám v BasePresenter::startup kontrolu, zda-li má uživatel povolení pro resource (kterým je název Presenteru) a privilege (kterým je název action či signálu), pokud ne, vyhazuji BadRequestException(‚msg‘, 403) :)

Jo, tohle zní dobře. Jenom se zeptám nette-nováčkovskej dotaz – jak zjistím v preparu presenter name a action/signal name?

Jinak přemýšlim, jestli neni lepší místo výjimky házet rovnou redirect na login presenter. Než abych pak výjimku chytal v bootstrapu a tam dělal redirect.

norbe
Backer | 405
+
0
-

V tom startup-u Article presenteru se volá parent::startup(). Musíš se tedy kouknout do metody startup v BasePresenteru…

Savannah
Člen | 30
+
0
-

ááá, sry, jsem slepej. Jasný, už to vidim, dík moc :)

Ondřej Mirtes
Člen | 1536
+
0
-

Jojo, můžeš místo BadRequestException dát flash zprávičku a přesměrovat na login presenter – a po přihlášení hodit uživatele zpátky na místo, kam se snažil přistoupit :)

Název Presenteru a action – $this->getName() a $this->getAction() :))

Savannah
Člen | 30
+
0
-

Tak jsem si s tím zkoušel trochu pohrát. Posílám to sem, kdyby vás to zajímalo nebo když byste mi postli, co tam mám špatně :) V Nette ještě neumím, takže jsem na spoustu věcí určitě nemyslel a pár nevím, jak udělat. Prvně nevím, jestli je ještě něco jiného než renderFoo() a handleFoo(), na co se dá z browseru pomocí url dostat. A za druhé jak správně dostat název action, kterou si uživatel vyžádal v url.

var_dump($this->getSignal());

mi vyhodí jenom

array
  0 => string '' (length=0)
  1 => string 'foo' (length=3)

(na index.php?do=foo). Tak jestli se mám spoléhat na to, že [1] je vždy název daného signálu nebo jak to vlastně je.

Jinak se snažím, jak už jsem psal, o něco jako má Django s anotacemi.

Url: BaseAuthPresenter / DemoPresenter

Ondřej Mirtes
Člen | 1536
+
0
-

Action získáš přes $this->getAction(), jak jsem psal.

Metody, které se volají při přístupu k nějaké action jsou actionName a renderName.

handleName slouží ke zpracování signálu :)

Savannah
Člen | 30
+
0
-

možná mám jenom bordel v názvosloví:

mám kód

<a href="{link foo!}">xxx</a>

což teda očividně neni action ale signal, že? Jak se dostanu na action? (resp. jaký je rozdíl oproti signal?) A jsou ještě jiné věci než render, action a signal? (jako na co se dá dostat pomocí URL) /vim, že sem to je asi trochu do jiné diskuse, ale píšu to sem, hodně se to váže k tomu, čeho se snažím dosáhnout a rád bych tu problematiku za tím pochopil/

Aurielle
Člen | 1281
+
0
-

Na action se dostaneš zápisem

<a href="{plink Module:Presenter:action}"></a>
Ondřej Mirtes
Člen | 1536
+
0
-

Klidně stačí i {link action} (bez vykřičníku, plink slouží k odkazování na Presenteru v komponentách :))

redhead
Člen | 1313
+
0
-

actiona se obecně mění bez vykřičníku, naproti tomu s vykřičníkem je to signál, a ten vede na stejný presenter a stejnou action, ale k tomu navíc vykoná handle<Signal>.

Oggy
Člen | 306
+
0
-

jenom dotaz pro inspiraci..
kam umisťujete ověření přihlášení uživatele?
dejme tomu že máme BasePresenter … kde jsou společné prvky pro Authpresentet (ve kterém je také loginForm) a pro další Presentery ..

Do BasePresenteru? tam asi ne.. z něj dědí i Auth
a ostatní presentery také dědí z Base..ale zase to nebudeme přece psát v každém kód na ověření znovu ..

Ondřej Mirtes
Člen | 1536
+
0
-

Do startupu v BasePresenteru. Klidně tam vyjmenuj výjimky, pro které se to ověřovat nemá.

Oggy
Člen | 306
+
0
-

Ondřej Mirtes napsal(a):

Do startupu v BasePresenteru. Klidně tam vyjmenuj výjimky, pro které se to ověřovat nemá.

Jak bys to provedl?

Není lepší v AuthPresenteru přepsat startup?

Ondřej Mirtes
Člen | 1536
+
0
-

Záleží, co chceš, aby u tebe představovalo resource a privilege… Já mám jako resource název presenteru a privilege název action…

Nástřel:

protected function startup() {
	parent::startup();
	$user = $this->getUser();
	if (!$user->isAllowed($this->getName(), $this->getAction())) {
		if ($user->isAuthenticated()) {
			throw new BadRequestException('Permission denied.', 403);
		} else {
			$this->flashMessage('Permission denied. Perhaps you forgot to log in.','error');
			$this->redirect('Auth:', array('backlink' => $application->storeRequest()));
		}
	}
}

V Auth můžeš přepsat startup, to je pravda :) Ale pozor, Presenter vyžaduje volání předka startupu, ale namísto parent::startup() můžeš zavolat Presenter::startup() a výjimka ti už nevyskočí :)

Případně to můžeš zpracovat přes anotace, pokud nechceš kontrolovat všechny Presentery a všechny actiony. Příklad takové kontroly je i tu někde na fóru.

Editoval Ondřej Mirtes (7. 1. 2010 20:56)

Oggy
Člen | 306
+
0
-

Ondřej Mirtes napsal(a):

Záleží, co chceš, aby u tebe představovalo resource a privilege… Já mám jako resource název presenteru a privilege název action…

Nástřel:

protected function startup() {
	parent::startup();
	$user = $this->getUser();
	if (!$user->isAllowed($this->getName(), $this->getAction())) {
		if ($user->isAuthenticated()) {
			throw new BadRequestException('Permission denied.', 403);
		} else {
			$this->flashMessage('Permission denied. Perhaps you forgot to log in.','error');
			$this->redirect('Auth:', array('backlink' => $application->storeRequest()));
		}
	}
}

V Auth můžeš přepsat startup, to je pravda :) Ale pozor, Presenter vyžaduje volání předka startupu, ale namísto parent::startup() můžeš zavolat Presenter::startup() a výjimka ti už nevyskočí :)

Případně to můžeš zpracovat přes anotace, pokud nechceš kontrolovat všechny Presentery a všechny actiony. Příklad takové kontroly je i tu někde na fóru.

děkuju..vyčerpávající

BigCharlie
Člen | 283
+
0
-

Pochopil jsem správně, že celý naznačený postup řeší i situaci s odeslaným formulářem ve chvíli, kdy mi vyprší přihlášení (tj. neztratím data z formuláře)? Projdu si tedy kolečkem odeslání formuláře → přesměrování na login presenter – přihlášení – obnova původního požadavku (odeslání formuláře). Takže po přihlášení uvidím výsledek po odeslání formuláře?

A jak to vypadá z flash zprávami, jak se ptal kravčo? Skutečne se některé zahodí, nebo se zobrazí všechny?

David Grudl
Nette Core | 8218
+
0
-

Po přihlášení se uvidí výsledek po odeslání formuláře. Ale faktem je, že to nemusí být úplně ideální řešení z uživatelského hlediska, že by třeba lepší bylo zobrazit vyplněný formulář a nechat jej uživatelem znovu odeslat. Co myslíte?