DyACL
An ACL library for handling database driven ACL.


Description
This is a really lightweight implementation of ACL. It can be used both for static and dynamic (Database Driven) ACL which was the main purpose of this library's creation.
Idea is, order of loading different rules - especially when they are loaded from database, e.g. user is allowed to change permissions - should not change the final calculated permission of a user.
But at first place why to invent the wheel again?! The answer is, there was a project of mine that needed ACL for managing access to different Files and Folders, which meant there will be lots of Rules that should be loaded from database. One of my friends suggested "Zend Permission Acl". I downloaded zend acl and went through its documentation, but it seemed vague, I was in shortage of time, and biggest probelm was order of loading different rules form databese would result in different final permission of a user! So here is my library. I will shed some light on "Why to use DyAcl instead of Zend ACL" later on.

If you're curious why I named it "DyAcl", I should say it's abbreviation of "Damn You ACL", because I had some difficulty understanding the ACL implementation process! :) Now it is "Dynamic ACL"!

Remember that this library does need just an array of anything so you're not forced to use database. I've also provided a class that extends from DyAcl and Uses "PDO" to load data from database.
If you need both or for the sake of change management, you'd better use the factory class.
Definitions
Visitor: Somebody who visits a page in your website.
User: A visitor who is a member of your website.
Role: A role of a visitor in your website. Roles are known as groups of users.
Resource: What ever stuff in your website that a permission can be applied to. It can be a controller, method in your controller, special function, a file, a directory, and etc.
Action: An action is the way a visitor accesses a part of your site it can include Create, Read, Update ,Delete.
Rule: A rule about who can access what and how.
How it works
1. This library should be loaded from namespace context. You can load it by using composer. If you are using lower versions of php which does not support namespace you need to replace namespaces with "include" statements.

2. Find out who is visiting your website. Each and every user even a guest should have an ID that will help you differenciate one from another.

3. Every user has at least one role and as many roles you desire is possible. Roles are different according to each site. It can be as simple as "Administrator, user" or it can be as complex as roles are defined by administrator on fly. But it makes no difference because you store these in "roles" table and when you find out what is your users ID it's as easy as a simple database query. Selected the roles from database, define them for library by "setRoles" function:

    $roles = array(); // here assign what you selected from "roles" table

    $dyacl = new DyACL();
    $dyacl->setRoles($roles);

if there is an special role that you desire to be added on fly add it by "setRole" function. For example you have a role named "admin" and you want to assign it to this user:

    $dyacl->setRole("admin");

It's better to use role_id if you need to load further roles later.

4. When you have a list of current user's roles you can select related "Rules" from it's table. After that define these rules for the library by using "setRules" function.

    $rules = array();// here assign the rules you selected from database.

    $dyacl->setRules($rules);

The library will define every resource that is mentioned by rules for itself so you do not need to define any resource explicitly. Access to a resource that is unknown for the library would be denied by default. In case you need to define any rule on fly go on and use "setRule" function. Each "rule" consists of a "resource" name, a "privilege", for which the possible values are 'allow' and 'deny', and finally an "action" which is by default "all" which means user is allowed to do whatever is possible. Other possible actions are Create, Read, Update and Delete.

For example you want to deny any access to a folder named "secrets":

    $dyacl->setRule("secrets", DyACL::DENY);

or you need to allow just viewing:

    $dyacl->setRule("secrets", DyACL::ALLOW, DyACL::ACTION_READ);

Remeber that DyACL::DENY is equal to "deny" and DyACL::ALLOW is equal to "allow".

5. Finally checking whether user has access to a resource or not is possible by "isAllowed" function:
For example after all this you want to check whether user is allowed access to the folder named "secrets" or not:

    if ($dyacl->isAllowed("secrets") {
        echo "Yes, you are allowed";
    }
    else {
        echo "Access Denied!";
    }

OR maybe you want to check whether the user is allowed to delete the folder "secrets":

    if ($dyacl->isAllowed("secrets", DyACL::ACTION_DELETE) {
        echo "Yes, you are allowed";
    }
    else {
        echo "Access Denied!";
    }

As I said previously there is a class extended from DyAcl class, named DyAclPDO, which uses PDO to load data from database:

    $dyAcl = new DyAclPDO("mysql:host={$sampleHost};dbname={$sampleDbName};", $sampleUsername, $samplePassword);

    $sampleUserId = 1;
    $dyAcl->prepareAcl($sampleUserId); //this function loads users' roles and related rules
                                       //from database

    $dyAcl->isAllowed('secret');

Instead of directly constructing DyAcl class it's better to use it's factory.

    $dyAcl = DyAclFactory::newAcl();

    or

    $dyAcl = DyAclFactory::newDyAclPDO($pdo, $configFile = null)
A Complete Sample
I have provided a sample file in DyAcl repo. I have also written some Unit Tests, taking a look at them may clear context of using this library.
Why to choose "DyACL" over "Zend Permission ACL"?
I guess this topic would be interesting for whom has already used "Zend Permission ACL" or other ACL libraries. What follows is what I found out by testing "Zend Permission ACL", if you can prove they're wrong, let me know And I will correct.

  1. Explicit Resource and role Definition

    In "Zend Permission ACL" roles should be defined explicitly and in order to define resource specific rules, resources should be defined previously. It means you should define all resources in a database table load them and add them to "Zend Permission ACL" and always maintain this table for extra resources or resources that do not exist anymore. What if you don't? The library will throw an Exception saying the resource has not been defined. This really complicates situation and is not memory efficient. But in DyAcl library itself adds resources whenever you define Rules.
  2. Rule Definition Context

    We know that for each resource all possible actions are Create, Read, Update and Delete (CRUD). In "Zend Permission ACL" When you do not provide "Privilege" for your rule, it assumes you mean every possible action, and when you are using "isAllowed" function with no privilege specified it checks for this rule. But imagine you have a user who is a member of 4 different groups and each one of these groups have a privilege for one specific action. when you ask "Zend Permission ACL" that if this user is allowed for all actions with leaving privilege parameter null in "isAllowed" function Zend says NO, means user is not allowed!!! So what to do? you have to define 4 different rules for each resource and check each rule separately to get the correct result which will cause a huge rule table and huge memory usage will follow.
    I've solved this in DyAcl. "all" keyword allows or denies access on all possible actions and you can check all access with the keyword "all" too. so its easy as pie!
  3. Order in "Allow" and "Deny"!!!

    In "Zend Permission ACL" Rule definition order is important! Means when you have two rules for one resource which one denies access and the other allows it, the result will be different depending on which one is loaded first! In "DyAcl" allow is more powerful, and by default everything is denied, so you just need to allow access to specific resources and order of rules does not matter anymore.
How to Use?
If you are using Composer in your project, it's pretty simple, just add the following line to your composer.json and do a composer update

    "require": {
        "ghost098/dyacl": "1.*"
    }


To learn more about Composer - What is it and how it works - you can check Composer website.

If you are not using composer in your project, depending on how you want to use this library you need to copy "DyAcl.php" and "DyAclPDO.php" to your project and include it in your file with autoload or requiring in place. Following examples will work for PHP >= 5.3

Example 1: including DyAcl:

    require_once "DyAcl.php";

    $dyacl =  new DyAcl\DyAcl();

    $dyacl->setRole(1);
    $dyacl->setRule('a', $dyacl::ALLOW);
    if($dyacl->isAllowed('a')) {
        echo 'yes';
    }


Example 2: including DyAclPDO:

    require_once "DyAcl.php";
    require_once "DyAclPDO.php";

    $sampleHost = "localhost";
    $sampleDbName = "DyACL";
    $sampleUsername = "testUsername";
    $samplePassword = "testPassword";

    $dyAcl = new \DyAcl\DyAclPDO("mysql:host={$sampleHost};dbname={$sampleDbName};", $sampleUsername, $samplePassword);

    $dyAcl->prepareAcl(1);

    echo "Checking 'public':<br>";
    if ($dyAcl->isAllowed('public')) {
        echo "Yes, 'all' action is allowed.<br>";
    } else {
        echo "No, 'all' action is not allowed.<br>";
    }

For PHP version < 5.3 you need to remove namespaces and include each class one by one.
Other Stuff
I will provide plugins for known php frameworks (Symphony2, Lithium, Silex, CakePhp, etc) ASAP.
You can clone this library here. Also you can download this library in zip format from here.

Finally, if you enjoyed this library go on and support it by starring it on github.
DyACL as CodeIgniter library
It's really simple to use this library in CodeIgniter Framework. What comes later is general way to install any library like DyAcl with composer.
Create a "composer.json" file like example below in root of your project - beside index.php:

    {
        "name": "ghost098/my-test-project",
        "description": "This is test project for running DyACL on CodeIgniter",
        "authors": [
            {
                "name": "Arash Tabriziyan",
                "email": "a.tabriziyan@gmail.com"
            }
        ],
        "version": "0.1.0",
        "require": {
            "php": ">=5.3.0",
            "ghost098/dyacl": "1.*"
        }
    }

Download composer with:

    curl -sS https://getcomposer.org/installer | php

run composer install in root of your project:

    ./composer install

add line bellow to end of your index.php:

    require_once APPPATH.'autoload.php';

FAQ
-Q: What happens if a "role" denies access to a resource but another one allows it?
-A: "allow" is more powerful than "deny" so result will be "allow".

-Q: Do I need to define each and every resource?!
-A: NO. There may be lots of resources defined in your database. Loading all that can use lots of memory or may be impossible for large projects. You just load specific rules that are related to current user and the library will handle all the rest.