ACL: Ako dostať Nette\Security\User do asercie?
- brano
- Člen | 25
Migrujem starší projekt na Nette 3.0. V ACL používam asercie, aby privilégia k zdrojom mal len vlastník. Na to asercia potrebuje poznať aktuálneho používateľa. Doteraz som to robil prasácky tak, že v aserci som si vypýtal \Nette\Environment::getUser(), zhruba takto:
<?php
class Owner_Assertion
{
public function assert($acl, $role, $resource, $privilege)
{
$user = \Nette\Environment::getUser();
$userId = ModelUser::getCurrentUserId($user);
$ownerId = $acl->getQueriedResource()->ownerId;
$result = ($userId === $ownerId);
if($result)
{
\Tracy\Debugger::dump("GRANTED");
}
else
{
\Tracy\Debugger::dump("DENIED");
}
return $result;
}
}
?>
To už teraz nejde. Oprávnenia vytvára inštancia triedy MyPermission, ktorá je definovaná ako služba v config.neon:
<?php
class MyPermissionFactory
{
/** @return Nette\Security\Permission */
static public function create()
{
return new MyPermission();
}
}
class MyPermission extends Nette\Security\Permission
{
public function __construct()
{
...
$this->allow(self::RoleMember,'article', 'edit', [new Owner_Assertion, 'assert'] );
...
}
}
?>
<?php
services:
authenticator: MyAuthenticator
- \MyPermissionFactory::create
?>
Skúšam teda využiť DI a do MyPermissionFactory doplniť závislosť na \Nette\Security\User, ale skončil som s cyklickou závislosťou (Circular reference detected for services: application.21, security.user, 01).
Ako to riešite vy? Nerád by som pri štarte aplikácie zisťoval, čo všetko daný používateľ vlastní a vytváral pre to stovky rolí. Dúfal som, že ACL sa bude vytvárať až on-demand v čase prvého použitia, a v tom čase už bude používateľ známy, a budem ho môcť nainjektovať v čase vytvorenia z DI kontajnera.
- brano
- Člen | 25
Rano múdrejšie večera. Všetky dotazy na ACL robím buď z presentera alebo zo šablóny, každopádne tam vytváram Resource. Najlepšie riešenie čo ma napadlo teda je – pribaliť \Nette\Security\User objekt do Resource objektu, a potom v asercii ju použiť. Logicky to tam síce nepatrí, le lepšie to momentálne spraviť neviem.
<?php
abstract class Owned_Resource implements Owned_ResourceInterface
{
private $user;
public function __construct( \Nette\Security\User $user )
{
$this->user = $user;
}
public function getUser(): \Nette\Security\User
{
return $this->user;
}
}
?>
<?php
class OwnerOrPowerUser_Assertion
{
public function assert(
\Nette\Security\Permission $acl,
string $role,
string $resource,
string $privilege
)
{
$user = $acl->getQueriedResource()->getUser();
$userId = ModelUser::getCurrentUserId($user);
$ownerId = $acl->getQueriedResource()->ownerId;
$result = ($userId === $ownerId) || MyPermission::IsPowerUser($userId);
if($result)
{
//Tracy\Debugger::dump("PERMITED");
}
else
{
//Tracy\Debugger::dump("DENIED");
}
return $result;
}
}
?>
Presenter:
<?php
$template->canEditSector = $user->isAllowed( new \Sector_Resource($this->getId(), $this->user), 'edit' );
?>