ACL: Ako dostať Nette\Security\User do asercie?

brano
Člen | 25
+
0
-

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
+
0
-

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' );

?>