Autentizace a autorizace pomocí anotací
- jiri.medved
- Člen | 33
Myslím, že mi to funguje, tak jak bych potřeboval, ale raději se chci poradit, zda-li je to takto vůbec správně.
namespace Medved\Security;
use Nette\Security\Permission;
class Authorizator extends Permission
{
public function __construct()
{
$this->addRoles();
$this->addResources();
// registered
$this->allow('registered', 'Homepage', 'default');
}
private function addRoles()
{
$this->addRole("guest");
$this->addRole("registered", "guest");
$this->addRole("admin", "registered");
}
private function addResources()
{
$this->addResource('Homepage');
}
}
namespace Medved\Application\UI;
use Nette\Application\ForbiddenRequestException;
use Nette\Security\User;
class Presenter extends \Nette\Application\UI\Presenter
{
/**
* {@inheritdoc}
*/
public function checkRequirements($element)
{
$user = $this->user;
if ($element->hasAnnotation("Secured")) {
if (!$user->isLoggedIn()) {
if ($user->getLogoutReason() == User::INACTIVITY) {
$this->flashMessage("Byl jsi odhlášen, protože jsi nebyl dlouho aktivní.");
}
$this->flashMessage("Pro vstup do této části webu se musíš přihlásit.");
$this->redirect("Sign:in", array("backlink" => $this->storeRequest()));
}
if ($element->hasAnnotation("Resource")) {
if (!$user->isAllowed($element->getAnnotation("Resource"), $this->action)) {
throw new ForbiddenRequestException();
}
}
}
}
}
/**
* @Secured
* @Resource("Homepage")
*/
class HomepagePresenter extends BasePresenter
{
public function renderDefault()
{
}
public function renderEdit()
{
}
}
- Ot@s
- Backer | 476
Principiálně to provozuji velice podobně (anotuji až na úroveň metod). Za přečtení stojí starý, ale stále platný příspěvek od Davida šikovnější permission (možná jsi ho už četl).
Edit: možná by stálo za to to dotáhnout a dát to do kuchařky
Editoval Ot@s (7. 6. 2012 9:56)
- miler
- Člen | 75
Pokud bych to chtěl posunout o úroveň níže na anotace metod (asi včetně privilegia v anotaci), stačí přepsat na:
class HomepagePresenter extends BasePresenter
{
/**
* @Secured
* @Resource("Homepage")
* @Privilege("Show")
*/
public function renderDefault()
{
}
a
(!$user->isAllowed($element->getAnnotation("Resource"), $element->getAnnotation("Privilege"))
a anotovat tedy místo celého presenteru „jen“ metody?
Doplňkový dotaz: V celém principu ACL mi totiž není moc jasné jak správně definovat ta privilegia. Většina ukázek a textů hovoří o privilegiích jako o akcích presenteru – tzn. pro každou akci přidat do ACL nové pravidlo?
Děkuji.
- Šaman
- Člen | 2659
Nechci zakládat nové téma, tak to píšu sem:
Existuje nějaké ‚oficiální‘ rozšíření na popis práv pomocí
anotací? Nebo jsou všechny anotace, které jsem dohledal pouze vlastní
přetížení metody checkRequirements
?
Nette 2.0.7 zná jen @User loggedIn
, na fóru a v dokumentaci
k anotacím jsem ale narazil i na @secured
,
@User (role=admin)
apod.
- enumag
- Člen | 2118
Ty příklady z dokumentace reflexí jsou pouze příklady nějakých anotací, netnamená to, že mají v Nette nějaký význam.
Nette samotné nic jiného než @User loggedIn opravdu nezná. V budoucnu možná něco více znát bude, ale to asi jen tak nebude. Předpokládám, že to bude tehdy až někdo ukáže dobrou komplexní implementaci zabezpečení na bázi anotací, což zatím nikdo neukázal. Já osobně takovou implementaci mám (a asi zdaleka nebudu sám), není to ale dostatečně připravené, abych to zveřejnil.
EDIT: O žádném oficiálním rozšíření rovněž nevím.
Editoval enumag (4. 12. 2012 13:51)
- enumag
- Člen | 2118
OK. :-) Já mám docela dobře udělaný nějaký věci kolem, ale implementace těch samotných anotací mi chybí, respektive je jen částečná a ne úplně taková jakou bych chtěl. Bohužel teď asi měsíc budu mít velmi málo času takže se rád inspiruji jinde a udělám z toho nějaký větší celek.
- pata.kusik111
- Člen | 78
Zatím neřeší odesílání formulářů, ale podchytí to jak action, tak handle. Navíc je možné udělat celý presenter Secured nebo mu přidat Resource a metody to od něj pak budou „dědit“. Funguje v Nette 2.0.14. Snad to někomu pomůže:
<?php
public function checkRequirements($element)
{
$user = $this->user;
$class = ($element instanceof \Nette\Reflection\Method) ? $element->getDeclaringClass() : $element;
$element = array_merge($class->getAnnotations(), $element->getAnnotations());
if (array_key_exists("Secured", $element)
) {
if (!$user->isLoggedIn()) {
if ($user->getLogoutReason() == User::INACTIVITY) {
$this->flashMessage("Byl jsi odhlášen, protože jsi nebyl dlouho aktivní.");
}
$this->flashMessage("Pro vstup do této části webu se musíš přihlásit.");
$this->redirect("Sign:in", array("backlink" => $this->storeRequest()));
}
if (array_key_exists("Resource", $element) && array_key_exists("Privilege", $element)) {
if (!$user->isAllowed($element["Resource"][0], $element["Privilege"][0])) {
throw new ForbiddenRequestException();
}
}
}
}
?>
- CZechBoY
- Člen | 3608
Určitě by mě to taky zajímalo. Já zatím používám tohle:
/**
* @var var $element
* @forward Homepage:|Error:blank
*/
public function checkRequirements($element) {
$this->startup(); // tu se nastaví storage, autorizátor
$class = $element->name;
$method = 'action' . $this->action;
$signal = $this->getSignal();
if ($signal) {
if ($signal[0]) {
$class = $this->getComponent($signal[0]);
}
$method = 'handle' . $signal[1];
}
try {
$c = new Nette\Reflection\Method($class, $method);
$fail = false;
if ($c->hasAnnotation('logged')) {
if (!$this->user->loggedIn) {
$this->flashMessage('Pro tuto akci musíte být přihlášen(a).', 'info');
$fail = true;
}
} elseif ($c->hasAnnotation('only_guest')) {
if ($this->user->loggedIn) {
$this->flashMessage('Pro tuto akci nesmíte být přihlášen(a).', 'warning');
$fail = true;
}
}
if ($fail === true) {
if ($this->isAjax()) {
$this->forward('Error:blank');
} else {
$this->forward('Homepage:');
}
}
} catch(\ReflectionException $e) {
}
}
Našel jste někdo chybu nebo něco co by se hodilo ještě kontrolovat?
Mělo by to umět jak kontrolu presenterů tak i komponent. Ale jsem tak trochu
vedle v tomhle odvětví tak bych si radši nechal poradit :-)
- enumag
- Člen | 2118
@CZechBoY Jsi mimo v tom že $element
co dostáváš
jako parametr už je reflexe (buď třídy nebo metody) čili je zbytečné abys
sám nějaké reflexe vytvářel. Volání startup je taky nežádoucí ačkoli
chápu proč to děláš (startup se volá až po checkRequirements pro třídu
presenteru). Ale jde to napsat i bez toho. Osobně v té metodě
checkRequirements jen všechno deleguju na službu která provádí samotné
kontroly.
Editoval enumag (5. 9. 2014 22:48)
- enumag
- Člen | 2118
@CZechBoY Ta metoda checkRequirements je volaná vícekrát. Poprvé pro třídu, poté postupně pro metody action*, handle* a render* – těsně předtím než je ta metoda reálně zavolána.
- enumag
- Člen | 2118
@CZechBoY Jednoduše, vůbec nezjišťuješ jakou reflexi jsi dostal, prostě se jí podíváš na anotace a zkontroluješ je. Jo a ta metoda checkRequirements nemá vracet true nebo false, pokud kontrola neprojde máš vyhodit výjimku, případně ukončit presenter pomocí forward/redirect/sendResponse/terminate (osobně doporučuji pouze vyhodit výjimku a tu pak zpracovat v ErrorPresenteru).
Pokud se chceš na něco zeptat přes IM, klidně můžeš. V nette konferenci na jabberu jsem pořád.
Editoval enumag (5. 9. 2014 23:40)
- ChocoTUx
- Člen | 31
Ahoj,
moc pěkná diskuze a rád si implementuji nějakou verzi anotací na
premission.
Každopádně ještě trochu bojuji s tím prvkem $element. Všude ho
používáte jako nějakou danou věc.
Dalo by se získávat takhle nějak (s tím že nechám volnou ruku jestli je secured nad třídou nebo metodou)?
$this->getReflection()->getAnnotations());
$this->getReflection()->getMethod('render'.$this->getView())->getAnnotations());