I have a Controller called "TestController" and several actions in it. For each action I receive a get Parameter ("id").
Something like:
test/action1/1
test/action1/2
test/action2/1
test/action3/1
...
Since this parameter can easily be modified I want to check the permissions for this id. Do I have to include this check method in each single action or is there another way? I know that I can't receive params in the constructor but a way like this would be nice.
My solution at the moment would be to have a check method in a plugin and call it in every action like this:
if(!$this->access()->checkAccess(..., $this->params()->fromRoute('id')) {
//redirect...
}
You can use ACL's (or RBAC) to make these checks.
With ACL's you can (must) declare the resources of your application, the roles that use the application and how the roles access the resources.
You can start by attaching a listener to controllers, in the application's Module.php
class Module {
public function onBootstrap(\Zend\Mvc\MvcEvent $event) {
$application = $e->getApplication();
$serviceManager = $application->getServiceManager();
$sharedManager = $application->getEventManager()->getSharedManager();
$router = $serviceManager->get('router');
$request = $serviceManager->get('request');
$matchedRoute = $router->match($request);
if (null !== $matchedRoute) {
$sharedManager->attach(\Zend\Mvc\Controller\AbstractActionController::class, \Zend\Mvc\MvcEvent::EVENT_DISPATCH, function($event) use ($serviceManager) {
$serviceManager->get('ControllerPluginManager')->get('Application\Controller\Plugin\Acl')->doAuthorization($event);
}, 2
);
}
}
}
As you can see, we attached the plugin Application\Controller\Plugin\Acl
to the dispatch
event of every Zend\Mvc\Controller\AbstractActionController
.
Then you have to implement your ACLS.
Here is a simple exemple.
By default, I prefer to deny access to all resources and then, one by one, allow access to them. You can also allow access to everything and then deny access to single resources, but this way you have to be more careful. If you deny all and forget something, an user will not be able to access something that it should be. If you allow all and forget something, user could see something that they shouldn't. Better be safe than sorry ;)
namespace Application\Controller\Plugin;
class Acl extends \Zend\Mvc\Controller\Plugin\AbstractPlugin implements \Zend\ServiceManager\ServiceLocatorAwareInterface {
private $serviceLocator;
public function doAuthorization(\Zend\Mvc\MvcEvent $event) {
// Retrieve user role, if you have them or just use guest
$role = 'guest';
$resource = get_class($event->getTarget());
$action = $event->getRouteMatch()->getParams()['action'];
if (!$this->getAcl()->isAllowed($role, $resource, $action)) {
// Handle the access denied
// Could be a redirect to login/home page, or just an 404 page
}
}
public function getAcl() {
// Create the ACL object
$acl = new \Zend\Permissions\Acl\Acl();
// Add your roles
$acl->addRole(new \Zend\Permissions\Acl\Role\GenericRole('guest'));
// Add your resources
$acl->addResource(new Resource(\YourModule\Controller\YourController::class));
// By default, block access to all resources.
$acl->deny();
// Add, one by one, access to resources for all roles
$acl->allow('guest', \YourModule\Controller\YourController::class, ['action1', 'action2'], new \YourModule\Assertion\YourIdAssertion());
return $acl;
}
public function getServiceLocator() {
return $this->serviceLocator;
}
public function setServiceLocator(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
}
The method allow
allows you to define these parameters:
- role (mandatory)
- resource (mandatory)
- actions (optional)
- assertion class (optional)
Finally, after declaring which roles can access a specific action of a specific resource, you can also tell the ACL a "rule", like "access the action only if it this condition is met". These rules are specified through Assertions
:
use Zend\Permissions\Acl\Assertion\AssertionInterface;
use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\RoleInterface as Role;
use Zend\Permissions\Acl\Resource\ResourceInterface as Resource;
class YourAssertion implements AssertionInterface {
public function __construct($serviceManager) {
// construct
}
public function assert(Acl $acl, Role $role = null, Resource $resource = null, $privilege = null) {
$isAllowed = false;
// Your logic to check if an user can access a specific id or not
// if(...){
// $isAllowed = true;
// }
return $isAllowed;
}
}
Hope this will help you!