Wednesday, December 4, 2013

How to CREATING ACL WITH DATABASE IN ZEND FRAMEWORK[Easy tricky]

Hey Guys see the creation of a secure and powerful ACL (Access control list) it’s one of the most delicate and important pieces for building a sturdy website.  I’ll try to make this task easier sharing the code I used in one of my latest projects. This ACL system works with a MYSQL database which grant us total flexibility creating users and roles.


CREATING DATABASE TABLES

The first step is to create the necessary tables in database:
Table roles
For storing the roles or groups. Each role have is own privileges. This roles will be assigned to each user, and the users will inherit the role privileges.
1
2
3
4
5
CREATE TABLE `roles` (
  `id` tinyint(1) NOT NULL AUTO_INCREMENT,
  `role` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
Now we add some roles to the table:Anonymous: For non-registered visitors.
Registered: For registered visitors.
Admin: For super users.
1
2
3
INSERT INTO roles (id, role) VALUES (1, 'Anonymous');
INSERT INTO roles (id, role) VALUES (2, 'Registered');
INSERT INTO roles (id, role) VALUES (3, 'Admin');
Table acl
To store all the controllers/actions. Each row means a different action that can be performed by the application.
1
2
3
4
5
6
7
8
CREATE TABLE `acl` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `controller` varchar(100) NOT NULL,
  `action` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `controller` (`controller`,`action`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
  CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC;
Table acl_to_roles
Establish the relation between the roles and actions. In other words: Which actions can perform each role/group.
1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE `acl_to_roles` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `acl_id` int(10) NOT NULL,
  `role_id` tinyint(10) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `acl_id` (`acl_id`),
  KEY `role_id` (`role_id`),
  CONSTRAINT `acl_to_roles_ibfk_1` FOREIGN KEY (`acl_id`)
     REFERENCES `acl` (`id`) ON DELETE CASCADE,
  CONSTRAINT `acl_to_roles_ibfk_2` FOREIGN KEY (`role_id`)
     REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Table users
It contains the field role_id, which establish the privileges that each user will inherit from the roles, and the general information about the users: login, password, ETC.
1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` tinyint(1) DEFAULT '1',
  `login` varchar(50) DEFAULT NULL,
  `password` varchar(32) DEFAULT NULL,
  `salt` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `login_index` (`login`),
  KEY `password_index` (`password`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
  CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC;

CREATE THE ACL PLUGIN

Now we need to create the ACL plugin. It will be located in the following path within our library folder: “/library/MyProject/Controller/Plugin/Acl.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
class MyProject_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
{
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        $auth = Zend_Auth::getInstance();
        //var_dump($auth->getIdentity());
        $authModel=new Application_Model_Auth();
        if (!$auth->hasIdentity()){
            //If user doesn't exist it will get the Guest account from "users" table Id=1
            $authModel->authenticate(array('login'=>'Guest','password'=>'shocks'));
        }
        $request=$this->getRequest();
        $aclResource=new Application_Model_AclResource();
        //Check if the request is valid and controller an action exists. If not redirects to an error page.
        if( !$aclResource->resourceValid($request)){
            $request->setControllerName('error');
            $request->setActionName('error');
            return;
        }
        $controller = $request->getControllerName();
        $action = $request->getActionName();
        //Check if the requested resource exists in database. If not it will add it
        if( !$aclResource->resourceExists($controller, $action)){
            $aclResource->createResource($controller,$action);
        }
        //Get role_id
        $role_id=$auth->getIdentity()->role_id;
        $role=Application_Model_Role::getById($role_id);
        $role=$role[0]->role;
        // setup acl
        $acl = new Zend_Acl();
        // add the role
        $acl->addRole(new Zend_Acl_Role($role));
        if($role_id==3){//If role_id=3 "Admin" don't need to create the resources
            $acl->allow($role);
        }else{
            //Create all the existing resources
            $resources=$aclResource->getAllResources(); 
            // Add the existing resources to ACL
            foreach($resources as $resource){
                $acl->add(new Zend_Acl_Resource($resource->getController()));
                     
            }      
            //Create user AllowedResources
            $userAllowedResources=$aclResource->getCurrentRoleAllowedResources($role_id);               
             
            // Add the user permissions to ACL
            foreach($userAllowedResources as $controllerName =>$allowedActions){
                $arrayAllowedActions=array();
                foreach($allowedActions as $allowedAction){
                    $arrayAllowedActions[]=$allowedAction;
                }
                $acl->allow($role, $controllerName,$arrayAllowedActions);
            }
        }
        //Check if user is allowed to acces the url and redirect if needed
        if(!$acl->isAllowed($role,$controller,$action)){
            $request->setControllerName('error');
            $request->setActionName('access-denied');
            return;
        }
    }
}

No comments:

Post a Comment