The examples on this site are currently tested to work on Phalcon V3.4 and Phalcon Devtools V3.2 Some issues may arise when using later versions.

Please get in touch or post a comment below the post if you encounter a problem.

The phalcon Security Plugin is a great tool which allows you to restrict access to Controller Functions to those users who are logged in and who are assigned to a role in the system which gives them the authority to use those functions.

This kind of access control is intrinsic to most multi-user system software which goes beyond a basic level of functionality. The only problem with Security Plugin is that as the number of different roles and Controller functions expands it becomes increasingly difficult to maintain the code in the Security plugin. As each new function is added a modification to each of the access control lists which specify who might be permitted access to that function is required.

Placing all this functionality in the database allows an administrator to quickly and easily assign who should have access to what functionality from the GUI interface of the application.

Use the following sql script to create the database tables which will store the information.

drop table if exists dbaccessControlList;
drop table if exists dbaction;
drop table if exists dbresource;
drop table if exists dbrole;

create table dbrole 
(
	role varchar(40),
	description varchar(160),
	primary key(role)
);

create table dbresource
(
	resource varchar(40),
	primary key(Resource)
);

create table dbaction
(
	resource varchar(40),
	action varchar(40),
	primary key(resource,action),
	foreign key(resource) references dbresource(resource)
);

create table dbaccessControlList
(
	role varchar(40),
	action varchar(40),
	resource varchar(40),
	primary key(role,action,resource),
	foreign key(role) references dbrole(role),
	foreign key(resource,action) references dbaction(resource,action)
);

insert into dbrole values('Registered User', 'Registered User privileges, granted after sign in.');
insert into dbrole values('Guest','Anyone browsing the site who is not signed in is considered to be a "Guest".');
insert into dbrole values('Admin','Administrator - can do everything');

The script allows for three sample roles Registered User, Guest, and Admin but additional roles can be added as required. By default everyone landing on the website is a 'Guest' until they login.

Now that the tables have been created we need to generate models. 

Create models for the dbrole, dbresource and dbaction tables but do a full scaffold on the dbaccesscontrollist table. Each of these components will be placed in the namespace "security".

phalcon model dbrole --get-set --namespace=security
phalcon model dbresource --get-set --namespace=security
phalcon model dbaction --get-set --namespace=security

Then scaffold the dbaccesscontrollist table:

phalcon scaffold dbaccesscontrollist --get-set --ns-models=security

In order that the application can see the new namespace "security" we need to register it in the application. Open the file /app/config/loader.php and add the following line to the registernamespaces section

"security"    => $config->application->modelsDir


This namespace can be modified to point to a different directory at a later stage should the application expand significantly but for now the models folder can serve as the registered namespace for "security". For a more detailed discussion on namespaces see here http://www.learnphalcon.com/post/show/7#namespaces

As each of the models, Dbresource, Dbaction and Dbrole were placed in the namespace "tennisClub" at the time they were generated we must now add use statements to the top of the DbaccesscontrollistController in order to be able to see these classes in a different namespace. Edit the file /app/controllers/DbaccesscontrollistController and add the following lines with the other use statements at the top.

use security\Dbresource;
use security\Dbaction;
use security\Dbrole;

To allow a user to quickly and easily modify the Access Control List we need to add some functionality. The following three functions should be added to the Dbaccesscontrollistcontroller which was generated by the scaffold.

The following function takes a resource (a controller) as an argument and generates a list of all the functions in that class and adds them to the list of actions for that resource in the database.

public function populateAclAction($resource)
{
    $dir = "../app/controllers/";
    $className = (ucfirst($resource . "Controller"));
    $controllerFile = $dir . $className . ".php";
    //tyring to include a file with the same name as the current script causes a conflict
    if(strcmp($resource,"dbaccesscontrollist")!=0) {
        if((@include $controllerFile) === false) {
            $this->flash->error("No such resource");
            return;
        }
    }
    $thisClass = new $className();
    $funcs = get_class_methods($thisClass);
    unset($thisClass);
    $resc = new Dbresource();
    $resc->setResource($resource);
    if (!$resc->save()) {
        foreach ($resc->getMessages() as $message) {
            $this->flash->error($message);
        }
        return;
    }
    //create an action in the database for each of the functions of the controller                    
    foreach($funcs as $func) {
        if(strpos($func,"Action")){
            $action = new Dbaction();
            $action->setResource($resource);
            $action->setAction(substr($func,0,-6));
            if (!$action->save()) {
                foreach ($action->getMessages() as $message) {
                    $this->flash->error($message);
                }
                return;
            }
        }
    }
    
}

The next function is associated with the view setAccessControl/resourceName. This function will call the populateAclAction($resourceName) function for that resource and set up role, resource, action and aclitem variables to be passed to the view.

public function setAccessControlAction($resource)
{
    $this->populateAclAction($resource);
    $this->view->resource=$resource;
    $this->view->roles = Dbrole::find();
    $this->view->actions = Dbaction::findByResource($resource);
    $this->view->aclItems = Dbaccesscontrollist::findByResource($resource);
}
The final function is called when the setAccessControl view is submitted with updated access control information. This information is processed and database is updated.

public function saveAccessControlAction()
{
    $resource = $this->request->getPost('resource');
    
    //delete all pre-existing access control settings for this resource
    $dbACLCurrentItems = Dbaccesscontrollist::findByResource($resource);
    foreach($dbACLCurrentItems as $dbACLCurrentItem) {
        $dbACLCurrentItem->delete();
    }
    $aclItemsArray = $this->request->getPost('aclItem');
    $msg = "No updates to the Acl";
    if(!empty($aclItemsArray)) {
        foreach($aclItemsArray as $role => $actionsArray) {
            foreach($actionsArray as $action => $y) {
                $aclItem=new Dbaccesscontrollist();
                $aclItem->setRole($role);
                $aclItem->setResource($resource);
                $aclItem->setAction($action);
                $aclItem->save();
                $msg="The Acl has been updated";
            }
        }
    }

    $this->flash->notice($msg);
    $this->dispatcher->forward([
        "controller" => "index",
        "action" => "index"
    ]);        
    
}

The final piece of the puzzle is the View itself. This allows the accesscontrollist to be presented as a grid of checkboxes which can be quickly and easily updated. This file should be called setAccessControl.phtml and saved under app/views/dbaccesscontrollist

<?php
/**
 * @var \Phalcon\Mvc\View\Engine\Php $this
 */
?>

<?php use Phalcon\Tag; ?>

<h3>Set Access Control for the <b><?php echo $resource ?></b> Controller</h3>
<?php echo $this->getContent(); ?>
<?php
    echo $this->tag->form(
        [
            "dbaccessControlList/saveAccessControl",
            "autocomplete" => "off",
            "class" => "form-horizontal"
        ]
    );
?>
<input type="hidden" name="resource" value="<?php echo $resource?>">
<div class="row">
    <table class="table table-bordered">
        <thead>
            <tr>
                <th></th>
                <?php foreach ($roles as $role): ?>
                    <th><?php echo $role->getRole(); ?></th>
                <?php endforeach; ?>
            </tr>
        </thead>
        <tbody>
        <?php foreach ($actions as $action): ?>
            <tr>
                <td><?php  echo $action->getAction(); ?></td>
                <?php foreach ($roles as $role): ?>
                    <td><?php
                        $thisAction = $action->getAction();
                        $thisRole = $role->getRole();
                        $hasAccess=false;
                        foreach ($aclItems as $item) {
                            if ($item->getRole()==$role->getRole() && $item->getAction()==$action->getAction() && $item->getResource()==$resource) {
                                $hasAccess=true;
                            }
                        }
                        if ($hasAccess) {
                            echo "<input type='checkbox' name='aclItem[$thisRole][$thisAction]' checked>";
                        }
                        else {
                            echo "<input type='checkbox' name='aclItem[$thisRole][$thisAction]'>";
                        }
                    ?></td>
                <?php endforeach; ?>
            </tr>
        <?php endforeach; ?>
        </tbody>
    </table>
</div>
<div class="form-group">
    <div class="col-sm-offset-2 col-sm-10">
        <?php echo $this->tag->submitButton(["Save", "class" => "btn btn-default"]) ?>
    </div>
</div>

<?php echo $this->tag->endForm(); ?>

Now when you call 

http://localhost/tennisClub/dbaccesscontrollist/setAccessControl/member

you get something like this