Zend Framework 2 ACL setup in 5 minutes – tutorial

March 13, 2013

In this tutorial you will learn how to setup zend framework 2 acl and check if user has access for current route.

If you don`t have testing project , download one from here.

Add file module.acl.roles in application/config/

Tip: In fact role resources may be zend framework 2 routes.

return array(
	'guest'=> array(
		'home',
		'login',
		'register'
	),
	'admin'=> array(
		'admin',
		'add-user',
		'delete-user'
	),
);

Code in application/module.php

public function onBootstrap(MvcEvent $e) {
	$this -> initAcl($e);
	$e -> getApplication() -> getEventManager() -> attach('route', array($this, 'checkAcl'));
}

public function initAcl(MvcEvent $e) {

	$acl = new \Zend\Permissions\Acl\Acl();
	$roles = include __DIR__ . '/config/module.acl.roles.php';
	$allResources = array();
	foreach ($roles as $role => $resources) {

		$role = new \Zend\Permissions\Acl\Role\GenericRole($role);
		$acl -> addRole($role);

		$allResources = array_merge($resources, $allResources);

		//adding resources
		foreach ($resources as $resource) {
             // Edit 4
             if(!$acl ->hasResource($resource))
			    $acl -> addResource(new \Zend\Permissions\Acl\Resource\GenericResource($resource));
		}
		//adding restrictions
		foreach ($allResources as $resource) {
			$acl -> allow($role, $resource);
		}
	}
	//testing
	//var_dump($acl->isAllowed('admin','home'));
	//true

	//setting to view
	$e -> getViewModel() -> acl = $acl;

}

public function checkAcl(MvcEvent $e) {
	$route = $e -> getRouteMatch() -> getMatchedRouteName();
	//you set your role
	$userRole = 'guest';

	if (!$e -> getViewModel() -> acl -> isAllowed($userRole, $route)) {
		$response = $e -> getResponse();
		//location to page or what ever
		$response -> getHeaders() -> addHeaderLine('Location', $e -> getRequest() -> getBaseUrl() . '/404');
		$response -> setStatusCode(404);

	}
}

Edit 1

I am assuming that all the routes that are accessed in the app are already added to the acl config file.

If not replace :

if (!$e -> getViewModel() -> acl -> isAllowed($userRole, $route)) {

to

if ($e -> getViewModel() -> acl ->hasResource($route) && !$e -> getViewModel() -> acl -> isAllowed($userRole, $route)) {

 

Edit 2

If access inheritance is not needed just replace this :

//$allResources = array_merge($resources, $allResources);
foreach ($allResources as $resource) {
// with
foreach ($resources as $resource) {

 

Edit 3

But what about acl from the database someone will say ?

In the zend framework 2 startup tutorial they say – “Don`t go heavy in onBootstrap”

With that said , I think that an array file will work for most of the cases.

Add this method where acl check is

    public function getDbRoles(MvcEvent $e){
        // I take it that your adapter is already configured
        $dbAdapter = $e->getApplication()->getServiceManager()->get('Zend\Db\Adapter\Adapter');
        $results = $dbAdapter->query('SELECT * FROM acl');
        // making the roles array
        $roles = array();
        foreach($results as $result){
            $roles[$result['user_role']][] = $result['resource'];
        }
        return $roles;
    }

Then replace in initAcl

// this
$roles = include __DIR__ . '/config/module.acl.roles.php';
// with
$roles = $this->getDbRoles($e);

Simple as that.
Suggestions or problems ? Write a comment.

tags: , , ,
posted in Guides, how to ?, php, Zend Framework 2 by Ivan Gospodinow

Follow comments via the RSS Feed | Leave a comment | Trackback URL

31 Comments to "Zend Framework 2 ACL setup in 5 minutes – tutorial"

  1. user1 wrote:

    Hi, great tutorial!

    Everything works fine, but when i go to route, that guest role does not have, i get an exception:

    Fatal error: Uncaught exception ‘Zend\Permissions\Acl\Exception\InvalidArgumentException’ with message ‘Resource ‘success’ not found’ in

    Acl\Acl->isAllowed(‘guest’, ‘success’) #2 [internal function]: Application\Module->checkAcl(Object(Zend\Mvc\MvcEvent))

    Something is not good about this line:

    if (!$e -> getViewModel() -> acl -> isAllowed($userRole, $route)) {

  2. user1 wrote:

    Oh, i just found solution :)

    Replace line

    if (!$e -> getViewModel() -> acl -> isAllowed($userRole, $route)) {

    to

    if (!$e -> getViewModel() -> acl ->hasResource($route) || !$e -> getViewModel() -> acl -> isAllowed($userRole, $route)) {

  3. Ivan Gospodinow wrote:

    Hello and thank you user1 ,

    yes you are right.The code from tutorial assumes that you have all the routes inserted in the acl (which is a good thing).That means you are controlling all the routes with roles.

  4. Andrey wrote:

    Hello.
    Thank you for great tutorial, but what about roles with different permissions?
    Looks like in your code each role is allowed also for resource listed in all previous roles in array.

  5. Ivan Gospodinow wrote:

    Hello Andrey ,

    thank you for your question.

    I created an Edit 2 for your case.
    By replacing those 2 lines of code , the roles will not inherit resources from one another.

  6. Dominic Watson wrote:

    Ctrl + F

    “-> ”
    Replace
    “->”

    ” ->”
    Replace
    “->”

    NO! Naughty! Bad Ivan! :P

    Nice and easy tutorial though :) makes me feel silly for thinking anything is difficult. I guess it’s getting used to the EventManager

  7. sandeep wrote:

    Hello,

    It,s working fine.
    but the function form controller is accessable by ajax , this only prents the view rendering.

  8. Tahmina Khatoon wrote:

    Thanks for nice tutorial, It is working fine everywhere except pagination with search. Would you please help me to set permission for following route.

    ‘people’ => array(
    ‘type’ => ‘segment’,
    ‘options’ => array(
    ‘route’ => ‘/people[/:action][/:id][/page/:page][/order_by/:order_by][/:order][/search_by/:search_by]‘,
    ‘constraints’ => array(
    ‘action’ => ‘(?!\bpage\b)(?!\border_by\b)(?!\bsearch_by\b)[a-zA-Z][a-zA-Z0-9_-]*’,
    ‘id’ => ‘[0-9]+’,
    ‘page’ => ‘[0-9]+’,
    ‘order_by’ => ‘[a-zA-Z][a-zA-Z0-9_-]*’,
    ‘order’ => ‘ASC|DESC’,
    ),
    ‘defaults’ => array(
    ‘controller’ => ‘WebApp\Controller\People’,
    ‘action’ => ‘index’,
    ),
    ),
    ),

    I have allowed ‘people’ for guest but it is not working with order_by and search_by parameters.

    Please help me.

    Thanks in advance

  9. Ivan Gospodinow wrote:

    Hello Tahmina ,

    in my example I am using route name as an acl resource.
    In your case you can do the following :

    $routeName = $e->getRouteMatch()->getMatchedRouteName();
    $routeParams = $e->getRouteMatch()-getParams();

    where route params are you root constraints – for example :
    $customResource = $routeName . $routeParams['controller'] . $routeParams['action'];

    Than you check like this :
    $acl -> isAllowed($userRole, $customResource);

    Simple as that.

  10. mhor wrote:

    (Noob question here) Just want to ask, how do you pass the role (line 39)?

    Tried it already but it always gives me a 404. But when I specify the //, it works normally

  11. Ivan Gospodinow wrote:

    Hello mhor ,

    two reasons to give you 404
    1. Resource is not added to the acl
    2. User is not allowed to access the resource.

    To your other question, you can easy access the session at that point and get the user credentials.If session does not exist you can assume that user is guest.

  12. Ivan Gospodinow wrote:

    Hello sandeep,

    it dont matter if the request is ajax.
    You should specify resouce(route) and user to access it in the acl.
    Module.php is executed every time.

  13. Prakash wrote:

    Hi,

    Thanks for this great tutorial.

    What in case, if i have to implement Zend2 ACL using Database define roles and access.

    Can you please help to implement acl using database. i search many links on google but no success on it.

    Thanks…

  14. Ivan Gospodinow wrote:

    Hello Prakash ,

    thank you for your question. I added edit3 to the article to give some idea of how to do it with a database.

  15. S.G. wrote:

    Hi Ivan,
    thanks for your sharing.

    I am confused about the “function checkAcl()” above.
    I had try to set myself file “module.acl.roles.php”, but my ACL seems not work.
    Would you explain the function checkAcl() more detail, and the purpose of line 39, “$userRole = ‘guest’;” ?

    Appriciate

  16. Ivan Gospodinow wrote:

    Hello S.G. ,

    basically checkAcl is called when route is initialised. We need this because acl roles are based on routes/resources (in other word – links).Then what we do is : get the current route name (you can add up action , controller and/or parameters) as a string and ask the acl class if user with role (exp. guest) can access this string (exp. login).

    Simple enought for you?

  17. Kathiravan wrote:

    Hello Ivan,

    i have three types of users admin, guest and user, have a page like login and register. here the three users will access the above pages
    return array(
    ‘guest’=> array(
    ‘add-user’,
    ‘login’
    ),
    ‘admin’=> array(
    ‘user-list’,
    ‘login’
    ),
    ‘user’=> array(
    ‘add-user’,
    ‘login’
    ),
    );

    but it shows the error Uncaught exception ‘Zend\Permissions\Acl\Exception\InvalidArgumentException’ with message ‘Resource id ‘login’ already exists in the ACL’ everything works fine except this plz help.

  18. Ivan Gospodinow wrote:

    Hello Kathiravan ,

    please take a look at Edit 4. Made it for you.

  19. Kathiravan wrote:

    Hi Ivan,

    There is no edit 4 displayed here.

  20. Kathiravan wrote:

    Hello Ivan,

    Sorry by mistake i posted the above comment Thanks its working now.

  21. Kumar wrote:

    Hi Ivan,

    Nice Tutorial.
    As I am a Beginner,
    Can You tell me the use of get Application() and get EventManager()?
    Thank You

  22. Ivan Gospodinow wrote:

    Hello Kumar ,

    getApplication return the Zend Mvc Application object, which is instantiated in index.php. It is basically the whole application.
    getEventManager is the object which can store and trigger events. See EventManager::attach() and EventManager::trigger();

  23. Kumar wrote:

    Thanks Ivan

  24. Kumar wrote:

    Can I know about get ServiceLocator(), viewhelpermanager ?

  25. Ivan Gospodinow wrote:

    Hello Kumar,

    you should not know about getServiceLocator in any view or view helper!

  26. Grisou wrote:

    To handle also allow and deny permissions I change this:

    Configs acl:
    // true to allow, false to deny
    return array(
    ‘guest’=> array(
    ‘home’ => true,
    ‘login’ => true,
    ‘register’ => true
    ),
    ‘admin’=> array(
    ‘admin’ => true,
    ‘add-user’ => true,
    ‘delete-user’ => true,
    ‘login’ => false,
    ‘register’ => false
    ),
    );

    And into Module.php:
    public function initAcl(MvcEvent $e)
    {
    $acl = new Acl();
    $acl->deny();
    $roles = include __DIR__ . ‘/config/module.acl.roles.php’;
    $allResources = array();
    foreach ($roles as $role => $resources) {

    $role = new \Zend\Permissions\Acl\Role\GenericRole($role);
    $acl->addRole($role);

    $allResources = array_merge($allResources, $resources);

    //adding resources
    foreach ($resources as $resourceName => $resourceAllowed ) {
    if(!$acl->hasResource($resourceName)) {
    $acl->addResource(new \Zend\Permissions\Acl\Resource\GenericResource($resourceName));
    }
    }

    //adding restrictions
    foreach ($allResources as $resourceName => $isResourceAllowed ) {

    if ( $isResourceAllowed ) {
    $acl->allow($role, $resourceName);
    } else {
    $acl->deny($role, $resourceName);
    }
    }
    }

    //setting to view
    $e->getViewModel()->acl = $acl;

    }

  27. bartek wrote:

    Hi, can it be done without redirection? I am developing restful application, and there are no restrictions available.
    Regards

  28. Ivan Gospodinow wrote:

    Hello Bartek ,
    you can change the layout with one that dispatches json/xml to tell the page that user do not have permissions to access it.

  29. Pragnesh Karia wrote:

    Hey ,

    Thanks for sharing a wonderful script.

    Can you please explain more in terms of ,

    “SELECT * FROM acl ORDER BY role_privileges DESC”

    what will be ACL table schema?

    what is the use of setstatuscode = 303 ?

    i want to redirect the to 403 access forbidden page (403.phtml)

    $response -> getHeaders() -> addHeaderLine(‘Location’, $e -> getRequest() -> getBaseUrl() . ‘/404′);
    $response -> setStatusCode(303);

    Any help would be appreciated.

  30. Ivan Gospodinow wrote:

    Hello Pragnesh Karia,

    you are right , we do not need role_privileges.I will fix that.
    For your database schema , you need at least user_role and resource. See the config array in the post.
    I will update the code to 404. I agree that its the right way!

    Thank you for you appinion.

  31. Pragnesh Karia wrote:

    Hello Ivan,

    Any more updates on the above , your help is highly appreciated.

Leave Your Comment

 
 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org