Nedaří se rozchodit statické ACL
- mr.mac
- Člen | 87
Můžete mi prosím poradit se statickým ACL (viz mnohé diskuse na tomto fóru), jsem bohužel „na začátku“ a nedaří se. Mám třídu ACL dle tutoriálu na tomto webu a nedaří se mi ověřit po přihlášení uživatele, zda jeho role isAlowed, skončím s chybou, že role „ABC“ does not exist, zde:
if (!$user->isAllowed($this->name, $this->action)) {
Je to tím, že role, resources i privileges nejsou ze statické ACL
načteny, přitom mám v config.neon službu autentizatoru definouvanou.
Kdy se tedy ACL načítá? V jaké části aplikace? Mě se totiž načte až
po, ale potřebuji před prvním použitím isAllowed.
Díky za rady.
- Filip Procházka
- Moderator | 4668
Jak to funguje
Pokud na ACL používáš Nette třídu Nette\Security\Permission,
tak ta se registruje jako služba authorizator
. Najdeš ji v DI
Containeru, nebo v presenteru
$this->context->authorizator
.
Zajímat tě bude u Permission
tato
stránka v dokumentaci.
Prvně tedy uživatele přihlásíš. V presenteru zavoláš Nette\Http\User::login()
s údaji (z formuláře), User
řekne službě
authenticator
(pozor, neplést authorizator
, který
spravuje oprávnění a authenticator
, který ověřuje identitu),
že chce ověřit uživatele. Tato služba ho ověří a vrátí identitu Nette\Security\Identity.
Identita obsahuje identifikátor uživatele, jeho role a nějaká dodatečná
data (adresa, email, …). Třída User
si tuto Identitu uloží do
Session a při dalších požadavcích už je k dispozici.
Když se teď zeptáš třídy
$user->isAllowed($resource, $privilege)
, tak se nejprve zeptá identity,
jaké má role a potom se ptá authorizatoru, jesti je to pro tu roli
povolena privilege
pro resource
.
Jak to zprovoznit
Čili tebe zajímají dvě služby:
- authenticator: Ověří uživatele a vrátí identitu. Tento si implementuješ sám (pokud nepoužíváš Nette\Database). Mělo by ti ale stačit doladit si implementaci v sandboxu
- authorizator: Na tento je vhodné si vytvořit nějakou továrnu (ať už taháš informace z databáze, nebo máš ACL definováno staticky. Zjednodušeně takto:
class AuthorizatorFactory extends Nette\Object
{
public function create()
{
$permission = new Nette\Security\Permission;
// ... nastavení podle dokumentace
return $permission;
}
}
Přidáš do configu
services:
authenticator:
class: Authenticator
arguments: [@connection]
authorizatorFactory:
class: AuthorizatorFactory
# arguments: [@connection]
# tady by jsi mu mohl pro konstruktor předat
# připojení do databáze, pokud by jsi ACL měl v db
authorizator:
factory: [@authorizatorFactory, create]
A třída User
už si to sama převezme a zpracuje.
- mr.mac
- Člen | 87
HosipLan napsal(a):
Jak to funguje
Díky moc za velmi vyčerpávající popis. Prozatím letmo přečteno si nemyslím, že jsem někde udělal extrémní chybu. Authorizator i Autenticator mám v config.nette spuštěny v sekci services takto (namespace zatím moc nevyužívám):
model:
class: Model
arguments: [@database]
authenticator:
factory: [@model, createAuthenticatorService]
authorizator:
class: Acl
Myslel jsem si to, že by to takto nějak mělo fungovat, problém je, že před prvním testem isAllowed jsou roles a resoucres z Acl třídy nenaplněný a tudíž systém hlásí, že role ABC neexistuje. Ještě to jednou projdu a pokusím se na to přijít. Je pravda, že návod (tuším od srigi-ho mj. pro nette 0.9 a já jedu na 2.0 + PHP 5.3) jsem musel dost silně modifikovat.
- mr.mac
- Člen | 87
Tak jsem to už rozchodil, musel jsem do SecuredPresentatoru dát instanci třídy ACL:
use Nette\Http\User;
abstract class SecuredPresenter extends BasePresenter
{
public function startup()
{
parent::startup();
$user = $this->getUser();
$acl = new Acl;
$user->setAuthorizator($aacl);
...
)
Nemyslím si, že to je moc košer, asi není v pořádku services: authorizator: class Acl v config.neon.
EDIT: Nakonec jsem to udělal dle rady HosipLana a chodí to výborně, není nad to si nechat dobře poradit. Ještě jednou díky moc.
Editoval mr.mac (21. 10. 2011 10:00)
- Filip Procházka
- Moderator | 4668
Tohle není moc ideální… Proč jsem ti asi psal, jak to nastavit v configu? :)
Pokud to nechceš v configu (taky tam nerad píšu služby), tak bych doporučil spíš podědit Nette\Configurator.
class MyConfigurator extends Nette\Configurator
{
public static function createServiceAuthorizator(Nette\DI\Container $container)
{
return new Acl();
}
}
Uložíš třeba do app/models/MyConfigurator.php
. V boostrap
pak musíš třídu načíst „ručně“, protože robotLoader
se
musí spustit až po Configuratoru.
...
require_once __DIR__ . "/models/MyConfigurator.php";
$configurator = new MyConfigurator();
// $configurator = new Nette\Configurator();
$configurator->loadConfig(__DIR__ . '/config.neon');
..
Configurator si projde všechny svoje metody, které začínají na
createService
(zbytek je název, jako u komponent v presenteru) a
přidá je jako továrničky do DI Containeru. Takže si můžeš přidávat
klidně vlastní služby takto. V argumentu metody můžeš přijmout
Nette\DI\Container
a vytáhnout si z něz parametry, nebo
závislosti. Příklad: https://github.com/…igurator.php
Editoval HosipLan (21. 10. 2011 9:31)
- srigi
- Nette Blogger | 558
Hosiplan viem, ze je to trocha provokativne, ale nedavalo by vacsi zmysel upravit povodny tutorial, nech nemusis radit ludom 100x? Myslel som, ze wiki forma webu sluzi tomuto, ale v nasej komunite to vobec nefunguje. Nechapem preco.
Editoval srigi (21. 10. 2011 20:52)
- Filip Procházka
- Moderator | 4668
Pravda, moc to nefunguje. Ale nechci na to sahat, kdybych to psal já tak bych to klidně upravil…
- 22
- Člen | 1478
@HosipLan: jaká výhoda je dědit od configuratoru a ne až od DI kontajneru, kam injektnu akorat služby, které potřebuji pro modely a ty mají potom svůj vlastní kontext, jako třeba connection apod.? Sice mám 2 kontexty, ale zapíšu $this->model->acl. Minimálně se mi to zdá lepší než tady hojně používaný modelLoader imho.
- Filip Procházka
- Moderator | 4668
22 napsal(a):
@HosipLan: jaká výhoda je dědit od configuratoru a ne až od DI kontajneru
Když dědím od Configuratoru a definuju služby tam, tak si nesviním třídu, do které to evidentně nepatří.
, kam injektnu akorat služby, které potřebuji pro modely a ty mají potom svůj vlastní kontext, jako třeba connection apod.? Sice mám 2 kontexty, ale zapíšu $this->model->acl. Minimálně se mi to zdá lepší než tady hojně používaný modelLoader imho.
Vůbec jsem tě nepochopil :)