我在acl.global.php
中配置了一个简单的ACL,如下所示:
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
]
]
],
]
];
解析器遍历配置,并从数组元素生成$this->allow($role, $controller, $action);
之类的Zend\Permissions\Acl#allow(...)
调用。
现在我还需要限制用户对项目的单一视图(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
)注入断言?
答案 0 :(得分: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;
}
}
答案 1 :(得分:0)
我不知道您是否可以应用我的解决方案,因为我在包含Zend \ Permission \ Acl的AclService类中配置我的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;
}
}
如果您不能直接应用此解决方案,我希望它可以指向正确的方向。