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.