Oprávnění na provedení akce s využitím více služeb

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

Ahoj,
obracím se na vás s takovým pro mne větším oříškem.

Začal jsem přemýšlet o tom, jak celou svojí aplikaci zabezpečit. Pokud bych měl jen jednu aplikaci a ta by neměla možnost nějakých modulů, tak je to celkem jednoduché. Stačilo by využít IAuthorizator::isAllowed() a bylo by po srandě. Ovšem já jsem šel cestou, že mám pár klientů a každý z nich má aplikaci s určitými moduly. Tudíž nikde není vše a každý klient má trochu něco jiného.

Napadlo mne to, že na takové to jádro aplikace bych využil právě IAuthorizator::isAllowed() a tam zapracoval celou logiku.

Ale jak na ty práva v modulech?
Třeba nyní si tu lámu hlavu nad tím, že chci řešit práva na soubory. Kdo může stáhnout daný soubor, či ho smazat, atp. To mohu vyřešit jednoduše právě v IAuthorizator::isAllowed(), ale co když mám modul, který mi tu logiku oprávnění ještě blíže specifikuje? Jako nejednodušší způsob by bylo využití „kontejneru“ a metody findByType(), ale to není správný přístup jaký bych měl asi i z pohledu FW využívat.

Věřím, že to tu někdo řešil a budu moc rád za každou radu.

CZechBoY
Člen | 3608
+
0
-

Mrkni se na metodu checkRequirements.

Tirus91
Člen | 199
+
0
-

@CZechBoY Ale ta je jen do presenteru, ne?

CZechBoY
Člen | 3608
+
0
-

jo, presenter je taky úplně na vrchu a má nad vším dohled.
Samozřejmě můžeš kontrolovat oprávnění až někde v modelech, vyhoď výjimku a v presenteru to nějak pěkně překonvertuj na user-friendly hlášku.

Tirus91
Člen | 199
+
0
-

@CZechBoY
abych pravdu řekl, tak asi moc nechápu.

Můj případ je to, že mám někde uložené soubory. V manageru, který obsluhuje právě práci s uložištěm bych si implementoval základní kontrolu oprávnění (např. anonym vs. přihlášený uživatel). Ovšem např. v DMS bych chtěl toto oprávnění rozšířit.

Již mám existující presenter :Front:File:get a ten mi obsluhuje právě získávání souborů. Tento presenter by daný soubor vrátil vždy, jelikož splňuje tu danou logiku.

Dle toho co mi radíš, bych si musel vytvořit ještě nový presenter (např. :Dms:File:get) a do něj si zapracovat novou logiku. Ale ten původní ve FrontModulu by stále soubor vrátil. Tím si přeci otevřu díru k uložišti.

Přijde mi lepší řešení, abych nějakým způsobem zapracoval dynamickou logiku přímo do daného FileManageru, skrze který se přílohy získají odkudkoliv.

Je možné, abych si v Nette service získal všechny služby, které implementují určité rozhraní? To by mi celý problém vyřešilo a bylo by to až dost dynamické. Co se týče ale návrhu, tak mi to právě nepřijde tolik čisté.

Co se týče daného rozhraní, tak jednoduchý příklad

<?php

interface FilePrivileges
{
    public function isAllowed($action, UserEntity $user, FileEntity $file);

}

Následně bych mohl vytvořit X Nette Service (každý mohl by mohl rozšiřovat kontrolu uprávnění), které by právě kontrolovali práva. (tohle právě nevím jak udělat v Nette service)

Ano, zde bych si mohl docela sám naběhnout a vytvořit si začarovaný kruh a daný soubor třeba nikdy už nestáhnu.

Doufám, že jsem to dostatečně vysvětlil a podaří se nám najít řešení. Nevěřím tomu, že podobnou situaci nikdo ještě neřešil :)


Ještě mne napadlo to řešit druhou možností. :Front:File:get presenter by vracel obsah jen u těch souborů, které by měli daný příznak (např. module=0). A každý modul, by měl presenter svůj, kde bych využil právě tebou zmiňovanou metodu Presenter::checkRequirements() nebo klidně i IAuthorizator::isAllowed().

Toto řešení se mi ale moc z pohledu DRY nelíbí. Ale asi je to nejjednodušší a nejrychlejší řešení.

Editoval Tirus91 (20. 3. 2017 20:53)

Jan Tvrdík
Nette guru | 2595
+
0
-

@Tirus91 Tak za prvé je potřeba si uvědomit, že rozhraní IAuthorizator nedává pro většinu aplikací smysl a přestal ho používat, viz třeba https://forum.nette.org/…r-a-identita

CZechBoY
Člen | 3608
+
0
-

Jestli máš ty úložiště po složkách a stačí ti kontrolovat jen jestli má uživatel právo k danému pložišti tak ti stačí obyč IAuthorizator s tím, že bys vyhazoval tu výjimku, prostě jak jsem psal minule.
Jestli potřebuješ každý soubor kontrolovat tak se nejsem jistej jestli brzo nenarazíš na nějaký výkonový problémy.

Jestli ti nevadí vytvářet nový presentery tak klidně vytvoř ten dms:file:get a překryj tam metodu checkRequirements (+zavolej parent) a udělej ověření podle toho modulu.

Tirus91
Člen | 199
+
0
-

@JanTvrdík Honzo díky moc za hint. Určitě si to pročtu a popř. opravím

@CZechBoY Ne, nemám to po složkách. Složky mám virtuální (vazby v DB), ale souborově je to na stejném principu jako má GIT.

Asi abych zajistil oprávnění pro každý model zvlášť, tak budu muset jít cestou skrze nové presentery. Už jen díky tomu, že s kontextem či kontejnerem ve službách či modelech nejde pracovat.

Oli
Člen | 1215
+
0
-

Nevím jestli jsem to pochopil dobře, asi ne.

Chceš nějak napsat rozhraní pro kontrolu oprávnění a kdyždý, kdo implementuje to rozhraní by prošel nějakou validací? Aby jsi to měl na jednom místě? Co když potom jednu třídu vytáhneš, implemntuješ rozhraní, ale už ne tu magii, která kontrolovala všechny rozhraní?

Je dost dobře možné, že odpovídám na něco jiného, ale zkusím to. :-)

Každý objekt by měl zodpovídat za svůj stav. Řekněme, že je nějaká FileService, která vrátí soubor. Její starostí by mělo být jaký soubor vrátí a jestli je v pořádku. Pak tu máme nějaký FileManager, který spravuje služby a ví, kdo má přístup k čemu. Jeho starostí by mělo být načíst soubor, zkontrolovat, jestli ho načíst můžu, a předat presenteru. Presenter by měl dostat soubor nebo výjimku (FileNotExists, NotAllowedAccess, …) a podle toho se zachovat (redirect na 403, 404, hláška, že soubor neexistuje, …)

Takhle bych to udělal. Jestli jsem odpovídal na něco uplně jiného, tak se omlouvám :)

Tirus91
Člen | 199
+
0
-

@Oli chápeš i nechápeš dobře :)

Ohledně Service a Manager to chápeme oba velmi dobře.

Chci si napsat rozhraní, které někdo implementuje. Daný manažer by si z DI vytáhl všechny služby, které právě toto rozhraní implementují a následně by je všechny volal. Jakmile by nějaký řekl, že daný uživatel na soubor má právo, tak bych ho vrátil. V případě, že bych projel všechny služby a žádná by neřekla, že na soubor má právo, tak bych následně vyhodil příslušnou výjimku.

Ber to hodně zrychleně a hodně špatně, ale věřím, že to pomůže

Metoda FileManager::retrieve() by mohla být nějak takto (psáno jako demonstrace)

public function retrieve(FileEntity $fileEntity, $response = false)
    {
        //tu bude nejaka ta kontrola napr.
        $permisionCheckers = $this->context->findByType('Tirus\\FilePrivileges');
        if (count($permisionCheckers) > 0) {
            foreach ($permisionCheckers as $permisionChecker) {
                if ($permisionChecker->isAllowed('get', $this->userEntity, $fileEntity)) {
                    if (!$response) {
                        return $this->getFilePath($fileEntity);
                    }
                    return new FileResponse(
                        $this->getFilePath($fileEntity), $fileEntity->getName(), $fileEntity->getMimetype()
                    );
                }
            }
        }
        throw new \Exception();
    }

Editoval Tirus91 (20. 3. 2017 21:42)

Oli
Člen | 1215
+
0
-

No a to je podle mě zbytečné. Nevím jaký máš use case, ale evidentně tvoje metoda vrací jeden soubor. A ty víš, jestli uživatel má přístup k souboru nebo ke službě. Tak bych zavolal jednu službu. Případně, pokud neprojde, tak další (s benevolentnějšími pravidly)

Pokud má manažer pracovat s více službami, tak si je injectnout a zkusit jednu po druhé. Rozhodně bych je ale nevytahoval podle interface, protože to je skrytá závislost.