2016-05-18 6 views
3

У меня есть простой ACL, конфигурирует в acl.global.php как это:Как построить ACL Assertion для значения переменной в Zend Framework 2?

return [ 
    'acl' => [ 
     'roles' => [ 
      'guest' => null, 
      'member' => 'guest', 
      'admin' => 'member' 
     ], 
     'resources' => [ 
      'allow' => [ 
       'Application\Controller\Index' => ['all' => 'member'], 
       'Application\Controller\Error' => ['all' => 'member'], 
       'Item\Controller\Process' => [ 
        'index' => 'member', 
        'create' => 'member', 
        'showItem' => 'member', // website.tld/item/:id 
        'showList' => 'member' // website.tld/list-items 
       ] 
      ] 
     ], 
    ] 
]; 

синтаксический анализатор итерацию по конфигурации и генерирует из элементов массива вызовы Zend\Permissions\Acl#allow(...) как $this->allow($role, $controller, $action);.

Теперь мне нужно дополнительно ограничить доступ пользователей к одному виду объекта (mydomain.tld/item/:id). Пользователь должен получить доступ только в том случае, если его id равен item.user_id (означает: пользователь является автором/владельцем).

Как мне реализовать это требование расширить конфиге

'Item\Controller\Process' => [ 
    'index' => 'member', 
    'create' => 'member', 
    'showItem' => [ 
     'role' => 'member', 
     'assertion' => 'UserIsOwner' 
    ] 
    'showList' => 'member' 
] 

и впрыснуть Assertion к Zend\Permissions\Acl#allow(...): $this->allow($role, $controller, $action, $assertion);.

namespace Authorization\Acl\Assertion; 
use ... 
class UserIsOwner implements AssertionInterface 
{ 
    protected $userId; 
    // To inject the $userId can be the job of the factory. 
    public function __construct(int $userId) 
    { 
     $this->userId = $userId; 
    } 
    public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) 
    { 
     return return $this->userId === ???; 
    } 
} 

Но теперь я понятия не имею, как утверждение должно получить item.user_id впрыскивается. Пример в docu не имеет этой проблемы, поскольку он имеет активы против $_SERVER['REMOTE_ADDR'].

можно впрыскивать ItemService выяснить item.user_id:

public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) 
{ 
    return $this->isUserOwner(); 
} 
protected function isUserOwner() 
{ 
    $itemId = ???; 
    $item = $this->itemService->findOne($itemId); 
    $itemOwnerId = $item->getUser()->getId(); 
    return $this->userId == $itemOwnerId; 
} 

Хотя тогда мне еще нужно внешние данные - текущий item.id.

В каком месте могут/должны содержаться данные переменной элемента (в данном случае item.user_id или item.id), к утверждению?

ответ

2

И наконец, я решил проблему, введя переменные данные через resource. Не думайте, что это самое чистое или рекомендуемое решение. Так или иначе, это работает. Но было бы неплохо узнать, как решить этот вопрос чистым/более элегантным способом.

UserIsOwner

namespace Authorization\Acl\Assertion; 

use Zend\Permissions\Acl\Assertion\AssertionInterface; 
use Zend\Permissions\Acl\Acl; 
use Zend\Permissions\Acl\Role\RoleInterface; 
use Zend\Permissions\Acl\Resource\ResourceInterface; 
use Item\Service\ItemService; 

class UserIsOwner implements AssertionInterface 
{ 

    /** 
    * 
    * @var integer 
    */ 
    protected $userId; 

    /** 
    * 
    * @var ItemService 
    */ 
    protected $itemService; 

    public function __construct(int $userId, ItemService $itemService) 
    { 
     $this->userId = $userId; 
     $this->itemService = $itemService; 
    } 

    public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) 
    { 
     return isset($resource->getParams()['id']) ? $this->isUserOwner($resource->getParams()['id']) : false; 
    } 

    protected function isUserOwner($itemId) 
    { 
     $item = $this->itemService->findOne($itemId); 
     $itemOwnerId = $item->getUser()->getId(); 
     return $this->userId == $itemOwnerId; 
    } 

} 

UserIsOwnerFactory

namespace Authorization\Acl\Assertion\Factory; 

use Zend\ServiceManager\FactoryInterface; 
use Zend\ServiceManager\ServiceLocatorInterface; 
use Authorization\Acl\Assertion\UserIsOwner; 

class UserIsOwnerFactory implements FactoryInterface 
{ 
    public function createService(ServiceLocatorInterface $serviceLocator) 
    { 
     $itemFieldsetService = $serviceLocator->get('Item\Service\ItemService'); 
     $authenticationService = $serviceLocator->get('AuthenticationService'); 
     $userId = !empty($authenticationService->getIdentity()['id']) ? $authenticationService->getIdentity()['id'] : null; 
     $service = new UserIsOwner($userId, $itemFieldsetService); 
     return $service; 
    } 
} 

ParametrizedResource

namespace Authorization\Acl\Resource; 

use Zend\Permissions\Acl\Resource\GenericResource; 
use Zend\Mvc\Router\Http\RouteMatch; 

class ParametrizedResource extends GenericResource 
{ 

    /** 
    * @var array Params. Here the RouteMatch#params. 
    * @see RouteMatch 
    */ 
    protected $params; 

    public function __construct($resourceId, array $params = []) 
    { 
     parent::__construct($resourceId); 
     $this->setParams($params); 
    } 

    /** 
    * 
    * @return the $params 
    */ 
    public function getParams() 
    { 
     return $this->params; 
    } 

    /** 
    * 
    * @param multitype: $params 
    */ 
    public function setParams($params) 
    { 
     $this->params = $params; 
    } 

} 

Acl

... 

// @todo refactor 
protected function addResources(array $resources) 
{ 
    foreach ($resources as $permission => $controllers) { 
     foreach ($controllers as $controller => $actions) { 
      if ($controller == 'all') { 
       $controller = null; 
      } else { 
       if (! $this->hasResource($controller)) { 
        $this->addResource(new Resource($controller, $this->routeMatchParams)); 
       } 
      } 
      foreach ($actions as $action => $roleConfig) { 
       if (is_array($roleConfig)) { 
        foreach ($roleConfig as $role => $assertion) { 
         if ($action == 'all') { 
          $action = null; 
         } 
         $assertion = !empty($this->assertions[$assertion]) ? $this->assertions[$assertion] : null; 
         if ($permission == 'allow') { 
          $this->allow($role, $controller, $action, $assertion); 
         } elseif ($permission == 'deny') { 
          $this->deny($role, $controller, $action, $assertion); 
         } else { 
          throw new \Exception('No valid permission defined: ' . $permission); 
         } 
        } 
       } elseif (is_string($roleConfig)) { 
        if ($action == 'all') { 
         $action = null; 
        } 
        if ($permission == 'allow') { 
         $this->allow($roleConfig, $controller, $action); 
        } elseif ($permission == 'deny') { 
         $this->deny($roleConfig, $controller, $action); 
        } else { 
         throw new \Exception('No valid permission defined: ' . $permission); 
        } 
       } 
      } 
     } 
    } 
    return $this; 
} 

... 

AclFactory

namespace Authorization\Acl\Factory; 

use Zend\ServiceManager\FactoryInterface; 
use Zend\ServiceManager\ServiceLocatorInterface; 
use Authorization\Acl\Acl; 

class AclFactory implements FactoryInterface 
{ 
    public function createService(ServiceLocatorInterface $serviceLocator) 
    { 
     $config = $serviceLocator->get('Config'); 
     $assertions = [ 
      'UserIsOwner' => $serviceLocator->get('Assertion\UserIsOwner') 
     ]; 
     $routeMatch = $serviceLocator->get('Application')->getMvcEvent()->getRouteMatch(); 
     $routeMatchParams = $routeMatch->getParams(); 
     $service = new Acl($config, $assertions, $routeMatchParams); 
     return $service; 
    } 
} 
0

Я не знаю, если вы можете применить мое решение, потому что я могу настроить Acl в AclService класса, который оборачивает в Zend \ разрешение \ Acl ,

В этом AclService я определил переменную $ assertions, которая представляет собой массив, в котором хранится объект каждого утверждения, которое я должен использовать.

namespace User\Service; 

use Zend\Permissions\Acl\Role\GenericRole as Role; 
use Zend\Permissions\Acl\Resource\GenericResource as Resource; 
use Zend\Permissions\Acl\Acl; 
use User\Service\Assertion\RightLeagueAssertion; 
use User\Service\Assertion\RightLeagueTeamAssertion; 

class AclService { 

    const ROLE_GUEST   = 'guest'; 
    const ROLE_MEMBER  = 'member'; 
    const ROLE_COMISSIONER = 'comissioner'; 
    const ROLE_ADMIN   = 'admin'; 
    const ROLE_GOD   = 'god'; 

    const ASSERTION_RIGHT_LEAGUE_TEAM = 'RightLeagueTeamAssertion'; 

    protected $acl = null; 

    protected $assertions; 

    /** 
    * Constructor 
    * 
    * @param Acl $acl 
    * @return void 
    * @throws \Exception 
    */ 

    public function __construct($acl) 
    { 
     $this->acl = $acl; 

     $this->assertions[self::ASSERTION_RIGHT_LEAGUE_TEAM] = $rightLeagueTeam; 

     /* Declaramos los roles */ 

     $this->acl->addRole(new Role(self::ROLE_GUEST)); 
     $this->acl->addRole(new Role(self::ROLE_MEMBER), self::ROLE_GUEST); 
     $this->acl->addRole(new Role(self::ROLE_COMISSIONER), self::ROLE_MEMBER); 
     $this->acl->addRole(new Role(self::ROLE_ADMIN), self::ROLE_MEMBER); 

     //unique role for superadmin 
     $this->acl->addRole(new Role(self::ROLE_GOD)); 

     /* Declaramos los recursos (module:controller) */ 
     $this->acl->addResource(new Resource('application:index')); 
     $this->acl->addResource(new Resource('application:error')); 
     $this->acl->addResource(new Resource('user:user')); 
     $this->acl->addResource(new Resource('leueroneyear:league')); 
     $this->acl->addResource(new Resource('leueroneyear:team')); 

     /*** Permisos ***/ 

     //'God' tiene permiso para todo  
     $this->acl->allow(self::ROLE_GOD); 

     //Una persona no logueada podrá ver solo el índice, errores, darse de alta y recuperar el password 
     $this->acl->allow(self::ROLE_GUEST, 'application:index', 'index'); 
     $this->acl->allow(self::ROLE_GUEST, 'user:user', array('register','forgotpassword','resetpassword','login')); 
     $this->acl->allow(self::ROLE_GUEST, 'application:error'); 

     $this->acl->allow(self::ROLE_GUEST, 'nba:test'); 

     //Los usuarios sí que podrán visitar las páginas 
     $this->acl->allow(self::ROLE_MEMBER, 'user:user', array('get','edit', 'logout')); 
     $this->acl->allow(self::ROLE_MEMBER, 'leueroneyear:league', array('index','get','list','add','enter')); 
     $this->acl->allow(self::ROLE_MEMBER, 'leueroneyear:team', array('get','add')); 

     $this->acl->allow(self::ROLE_MEMBER, 'leueroneyear:team', 'index',$this->assertions[self::ASSERTION_RIGHT_LEAGUE_TEAM]); 

    } 

    public function getAcl() 
    { 
     return $this->acl; 
    } 

    public function isAllowed($role, $controller, $action) 
    { 
     $a = explode("\\",$controller); 
     $resource = strtolower($a[0]).":".strtolower($a[2]); 
//\Zend\Debug\Debug::dump($resource); die(); 
     return $this->acl->isAllowed($role, $resource, $action); 
    } 

    public function setRequestParams($params) 
    { 
     $a = explode("\\",$params["controller"]); 
    $controller = strtolower($a[2]); 

    switch ($controller) { 
     case 'team': $this->assertions[self::ASSERTION_RIGHT_LEAGUE_TEAM]->setRequestParams($params); 
      break; 
    } 
    } 

} 

Когда время, чтобы проверить, если кто-то может использовать ресурс, я впрыснуть параметры маршрута согласованного в AclService, который впрыскивает их в каждом классе утверждение ранее реализованным (функция «setRequestParams»).

/** 
* @param MvcEvent $e 
*/ 
public function onRoute(MvcEvent $event) 
{ 
    $matches = $event->getRouteMatch(); 

    $controller = $matches->getParam('controller'); 
    $action = $matches->getParam('action','index'); 

    $auth = $this->authService; 

    /* @var $user User\Entity\User */ 
    if ($user = $auth->getIdentity()) { 
     $session = new Container("League"); 
     if (isset($session->isCommissioner) && $session->isCommissioner) 
      $role = AclService::ROLE_COMISSIONER; 
     else 
      $role = AclService::ROLE_MEMBER; 
    } else { 
     $role = AclService::ROLE_GUEST; 

    } 
    $acl = $this->aclService; 
    $acl->setRequestParams($matches->getParams()); 

    if (!$acl->isAllowed($role,$controller, $action)) { 

     //El usuario no tiene los permisos necesarios 

     $app = $event->getTarget(); 
     $route = $event->getRouteMatch(); 

     $event -> setError(RouteGuard::ERROR) 
       -> setParam('route', $route->getMatchedRouteName()); 

     $app->getEventManager()->trigger('dispatch.error', $event); 

    } 

} 

Таким образом, вы можете получить доступ к этим параметрам в своих классах утверждений.

class RightLeagueTeamAssertion implements AssertionInterface 
{ 
    protected $requestParams; 

    public function setRequestParams($params) 
    { 
     $this->requestParams = $params; 
    } 


    /** 
    * Comprueba que el idTeam que pasan por parámetro pertenece a la liga en la que estás logueado 
    */ 
    public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null) { 

     $appSession = new Container("Application"); 
     $leagueSession = new Container("League"); 

     $idLeague = $leagueSession->idLeague; 

     $idTeam = $this->requestParams['id']; 

     \Zend\Debug\Debug::dump($idTeam); 

     return false; 
    } 

} 

Если вы не можете применить это решение напрямую, я надеюсь, что оно может указывать на правильное направление.

 Смежные вопросы

  • Нет связанных вопросов^_^