Table of Contents

Using security features of TaniPHP

From the security point of view, you can have 3 types of controllers:

  1. “Unsecure” controller (default) - all actions in the controller can be accessed by anyone.
  2. Secure controllers - all actions in the controller can be accessed only by logged on users.
  3. Privilege based access control - fine-grained control - each action might have it’s own specific privilege requirements.

Most of the time, the 3rd option will be used together with the 2nd one, which will result in a controller that requires an authenticated user and might allow only specific users to some of it’s actions. Please note that you might have a secure controller, and specify special privileges for only some of it’s actions - the rest of the actions will require just a logged on user.

The next examples will show how I set my login “workflow” for my intranet app. The default controller/action points to a secure controller which means that, upon accessing http://intra.net.site, the user will be redirected to another controller/action used for logging in. You can change the controller/action it’s redirected to in the config.ini.php file (see secureController/secureAction). When the user enters his/her password and submits the form, I check in another action if the user is ok, and let the framework know that the user has logged on (see User::login()), then I redirect him back to the default controller/action. This time, the user is logged on, and the page is displayed.

Allowing users to login

Create a controller (eg. secure_controller.php) that will be used to login.

<?php 
class Secure extends ActiveController
{
 
    // I don't want to use the default layout for the login page. Mainly because it exposes what menus I have in my intranet app.
    var $layout = ""; 
 
    // You want to use your custom users table to check if the user is ok. For this example, I'm using the Operators table.
    var $models = array('operator');
 
    function login()
    {
        // show login form
    }
 
    function dologin()
    {
 
        $op = new Operator;
 
        // findRor always returns an array
        $ops_array = $op->findRoR('name', trim($_POST['username']));
 
        if(is_array($ops_array)) {
            reset($ops_array);
            list($id, $op) = each($ops_array);
 
            if( ($op->id > 0) && (md5($_POST['password']) == $op->password) ) {
 
                // Set the user as logged on. Save the ID and username for further use.
                User::login($op->id, $op->name);
 
                // redirect to default page
                Framework::redirect();
                return;
            }
 
        }
 
        // if the checks failed, redisplay the form
        Framework::redirect('secure', 'login');
 
    }
 
    function logout()
    {
        User::logout();
        Framework::redirect('secure', 'login');
    }
 
    // used by dispatcher to show "privilege required" errors
    function noprivileges()
    {
        Framework::croak("No privileges to access this page");
    }
 
}
?>

Securing a controller

To create a controller that can be accessed only by an authenticated user:

<?php
class BillingReports extends ActiveController
{
    var $secure = true; // require logged on user; will automatically redirect to login page otherwise
}
?>

Restricting actions

To restrict actions in the controller to users with specified privilege:

<?php
class BillingReports extends ActiveController
{
    var $secure = true; 
    var $restrictions = array(
           'action1' => 'billing-privilege1',
           'action2' => 'billing-privilege2'
    );
 
    function action1() {}
    function action2() {}
 
}
?>

Setting up privilege tables

The ideas for this part are taken from the phpGACL project. The table structure is found in the taniphp/acl_aco_aro_tables.sql file.

ACO - Access Control Objects

In the aco table you should store the access control objects. For our BillingReports controller, the ACO table will look like this:

id name
1 billing-privilege1
2 billing-privilege2

ARO - Access Request Objects

Here you should store the groups/users in your system. Please note that this means some of the information is duplicated in your users table. You might use the aro table for both purposes, though I haven’t tryed that. Let’s create the Management and Users groups, with 2 users in each group.

id name left right
1 Users 1 12
2 Management 6 11
3 simple_user1 2 3
4 simple_user2 4 5
5 manager1 7 8
6 manager2 9 10

The left and right fields are used for storing the groups/users tree in the table. Please check the article about storing hierarchical data in a database.

Basically, the Users group contains all users in the system. Inside it, there are 2 users + the Management group, that contains another two users. If you set some privilege to the Users group, all the users, including the managers will have it. If you set a privilege to the Management group, only manager1 and manager2 will have that specific privilege.

ACL

Here comes the complicated part. We have to configure which users/groups have access to which objects (in other words, what privileges does each user/group has).

For this example, let’s implement this:

The ACO table will look like this:

id aro aco value enabled
1 1 1 1 1 Users(aro=1) have access to action1(aco=1)
2 3 1 0 1 simple_user1(aro=3) doesn’t have access to action1(aco=1)
3 2 2 1 1 Management(aro=2) has access to action2(aco=2)

Please note that since Management group is in the Users group, all the managers will have access to both action1 and action2.

Having all 3 rows in the database sets the privileges to all the users in our system. When adding a new user, you just have to place it in the group you need and all permissions/restrictions for that group will apply to him/her too.