Znovupoužitelné komponenty a autorizace
- jannek19
- Člen | 47
Ahoj,
dejme tomu, že mám komponentu pro výpis seznamu uživatelů, u každého uživatele je tlačítko pro jeho smazání. Tlačítko obsahuje odkaz na signál komponenty, který provede smazání uživatele z DB. Jak co nejlépe ověřit, že má právě přihlášený uživatel právo konkrétní záznam smazat? Komponenta by měla být znovupoužitelná a připravená na různé metody ověření přístupu.
Napadají mě následující řešení:
- napsal bych si autorizátor implementující dohodnutý interface. Komponenta by vždy vyžadovala injectnutí odpovídající služby (autorizátoru) a ověření by delegovala na tuto službu
- komponenta by nevěděla nic o žádném autorizátoru, jen by poskytovala
událost
onAuthorize
a při ověření oprávnění by vždy odpálila tuto událost. Callbacky navěšené na tuto událost by ověřily oprávnění a pro odepření přístupu by vyhodilyForbiddenException
.
Která varianta je podle vás vhodnější?
Editoval jannek19 (6. 8. 2014 22:30)
- Šaman
- Člen | 2659
S tím zase nesouhlasím já. Model není jen nějaký dotazovač, tím je ORM. Model by měl být jádro aplikace, který si sám hlídá konzistenci a chrání bezpečnost. Modelu je sice jedno, jestli se uživatel identifikuje heslem, tokenem, nebo čím chce. Ale jestli ten uživatel může něco dělat, to by si měla hlídat nějaká vrstva hned nad repository. (Ano, repository se má jen starat o db (resp. úložiště) a nikoliv o oprávnění. Ale model ≠ repository.)
Jinak oprávnění na zobrazení komponenty/pohledu si samozřejmě řeší presenter. Ale i kdyby se zobrazilo třeba mazací tlačítko uživatelovi, který nemá práva mazat, tak to nesmí ohrozit bezpečnost modelu.
Editoval Šaman (7. 8. 2014 7:00)
- jannek19
- Člen | 47
Díky za odpovědi. Svým způsobem mi nová vrstva mezi komponentou a samotným repository, která se by se postarala o autorizaci dává smysl, ale nejsem si jistý, že to řeší můj problém. Kontrolu v továrničce v presenteru samozřejmě provedu, ale tím maximálně zabráním tomu, aby se ta komponenta nevytvořila, neřeší to problém, kdy mám v komponentě signál a právě v něm musím rozhodnout jestli má uživatel právo operaci dokončit, nebo ne. Logika celé operace je v komponentě, data, o kterých rozhoduji, jsou v komponentě, tak musím podle mě v komponentě také rozhodnout, nebo toto rozhodnutí delegovat někam dál. Ale právě si moc nevím rady s konkrétním řešením :-/ Ale ještě nad tím popřemýšlím, třeba je ta vrstva mezi komponentou a repository právě to, co hledám.
- Zax
- Člen | 370
To bohužel řeší jen „smí/nesmí vytvořit komponentu“. Často ale chceme přímo v komponentě rozhodnout, jestli něco zobrazíme jako text, nebo text s odkazem na editaci. Nebo se chceme rozhodnout, jestli zobrazit editační/mazací tlačítka v seznamu, ale nechceme kvůli tomu dělat samostatnou komponentu (důvody neřešme).
Osobně používám metodu 1. Určitě existují i lepší řešení, ale přijde mi to vhodné („good enough“). Můžu se pak na oprávnění dotazovat i v šabloně (byť to není úplně ideální).
Ještě jsem přemýšlel (v podstatě metoda 1. jen trochu konkrétnější), že bych pro každou komponentu udělal vlastní něco-jako-authorizátor, v podstatě službu, která bude mít konkrétní metody (definované třeba už v interface, aby šlo libovolně měnit implementaci) určené na dotazování z komponenty např.
canEditAllArticles()
canEditArticle($id) // nebo canEditArticle(Article $article)
canDeleteAllArticles()
...
A tento autorizátor by se mohl vnitřně dotazovat jak na Nette authorizátor, tak třeba i na model (uživatel smí upravovat a mazat jen své články, admin smí vše…).
Osobně by mě taky zajímalo nějaké pěkné univerzální řešení.
Editoval Zax (9. 8. 2014 17:42)
- Jan Suchánek
- Člen | 404
Me se libi resit to eventem signalu komponenty napr. beforeDelete primo v prezenteru.
Prezenter rozhodne a komponenta nemusi resit pri signalu nic.
- Šaman
- Člen | 2659
Pokud řešíš viditelnost třeba mazacích tlačítek, tak samozřejmě komponenta potřebuje authorizator. Je to naprosto relevantní požadavek. Komponenta, nebo presenter, to je jedno, oba by si měli hlídat práva, jen si myslím, že tam dojde snadněji k chybě (mnoho kontrol na různých místech), než v modelu, kde všechny požadavky na repository nejdřív profiltruje vrstva která má authorizátor. Takže tu vrstvu v modelu beru jako hlavní, která ručí za konzistenci dat, a tu kontrolu v presenteru/komponentě spíš jako frontendovou záležitost.
- jannek19
- Člen | 47
Event se mi taky na pohled celkem dost líbí, problém je v tom, že se na něj nemůžeš spolehnout. To pak přichází v úvahu ještě nějaká autorizační vrstva mezi komponentou a modelem (jak popisuje např. Šaman), která ti zaručí, že nedojde k operaci, ke které dojít nemá. Problém s eventem je ten, že nesmíš zapomenout na něj navěsit v presenteru (nebo někde jinde) odpovídající kontroly – 100× nezapomeneš, ale po 101 můžeš a hned je bezpečnostní díra na světě. Takže event maximálně jako nějakou doplňkovou záležitost, která třeba zabrání vykreslení nějakého prvku (odkazu, tlačítka), ale nesmíš na ní přenést celou odpovědnost za autorizaci.
- Jan Suchánek
- Člen | 404
@jannek19 No já jsem ten event řešil přímo u komponenty při tvorbě v presenteru.
protected function createComponentExample(){
$control = $this->exampleFactory->create();
$control->onBeforeAction[] = $this->checkSecurity; // a beforeAction provedu vdyž před akci tím pádem komponenta jí nebude muset řešit.
return $control;
}
Lde bych na to zapoměl? V komponentě na to mohu zapomenout uplně stejne nebo ne?
Editoval jenicek (18. 8. 2014 16:52)
- Jan Suchánek
- Člen | 404
Nechtělo se mi to řešit přez Kdyby\Events, a myslel jsem to nějak takto:
class ExamplePresenter
{
protected createComponentExample()
{
$control = $this->exampleFactory->create($this->user->id);
$control->onRestriction[] = function($control){
if($isAllowed = $this->user->isAllowed( ... )){
$control->setEditable();
}
};
retrun $control;
}
}
a v komponentě už jen pouštet $this->onRestriction() a v ty restrikci by se dali předávat i ty parametry restrikce, ale asi je to zbytečné delegování zodpovědnosti někam jinam, a zatěžování presenteru.
Možná má tedy ten Šamanuv způsob řešit restrikce přímo v modelu něco do sebe.
Editoval jenicek (20. 8. 2014 14:40)
- Jan Suchánek
- Člen | 404
Ok presne to delam tim editable, ktere predavam do sablony komponenty eventem a v komponente tedy nepracuji s autorizaci. Je to blbe?
Mozna staci jen setEditable pro komponentu a defaultne ji mit needitovatelnou.
Editoval jenicek (21. 8. 2014 10:40)