Monday, April 8, 2013

Refining Access Rules

Access Rules in your various controllers is a great way to control which user is able to perform specific actions. The common layout of the access rules is something like: 

public function accessRules()
{
return array(
array('allow',  // allow all users to perform 'index' and 'view' actions
'actions'=>array('index','view'),
'users'=>array('*'),
),
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions'=>array('create','update', 'transfer',),
'users'=>array('@'),
),
array('allow', // allow admin user to perform 'admin' and 'delete' actions
'actions'=>array('delete'),
'users'=>array('admin'),
),
array('deny',  // deny all users
'users'=>array('*'),
),
);
}
This is fine for many applications however sometimes there is a need to further restrict access to custom functions that are created in your controller when there are different types of users that have access to your web application. A typical example is that you have a superuser (Admin), another set of users (Clerical) and then you may allow clients to access your web application as well. In the Access Rules above we have only catered for authenticated users, all users and admin. A client is an authenticated user however you may only want to allow the client to be able to perform one action. One way to do this is by making use of the Identity class which is used in performing user identification.

Typically all users will have their details stored in the database and I usually assign them a role in the "user" database table. For example the field called role may be

User role
User Type Role
Admin
1
Clerical
2
Client
3

Now that each user that is set up in the database has a role we can set the users role that persists throughout the current users session by modifying the UserIdentity Class. In the UserIdentity Class we can create any characteristic we may want to make use of that will persist while the user is logged in.  In the following example I call setState() to store specific information about the user such as their "role", "id" and "username". The syntax to set this information is: $this->setState('variable_name', $record->database_field). Notice the database query for the "User" table gets the required attributes.


class UserIdentity extends CUserIdentity
{
    private $_id;
    public function authenticate()
    {
        //************Database query of User table*************************
        $record=User::model()->findByAttributes(array('username'=>$this->username));

        $tstvar = sha1($this->password);

        if($record===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if($record->password!==$tstvar)

            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
        {
            $this->_id=$record->id;
            $this->setState('title', $record->real_name);
            $this->errorCode=self::ERROR_NONE;
            // Store the role in a session:
            $this->setState('role', $record->user_type_id);
            $this->setState('userid', $record->id);
            $this->setState('uname', $record->username);
        }
        return !$this->errorCode;
    }

    public function getId()
    {
        return $this->_id;
    }
}
Now it is easy to retrieve this information once it has been set by using
$currentUser = Yii::app()->user->role;

This can be very useful if you want to have a different menus for users with a different role. An admin user may be able to see a link to an audit trail however you may not want clerical staff to be able to access this information. There are many ways to exploit this attribute and further customise your web application.

However the point of this was to further refine the actions users are able to perform within the different controllers. To make use of the users role to further define what actions that particular user is able to perform take a look at the following example:

public function accessRules()
{
return array(
array('allow',  // allow all users to perform 'index' and 'view' actions
'actions'=>array('index','view'),
'users'=>array('*'),
),
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions'=>array('create','update', 'process', 'complete', 'progress',),
'users'=>array('@'),
'expression'=>'isset($user->role) && ($user->role!=3)',
),

   array('allow', // allow authenticated client to perform 'transfer' and 'notice' actions
'actions'=>array('transfer','notice'),
'users'=>array('@'),
'expression'=>'isset($user->role) && ($user->role==3)',
),

array('allow', // allow admin user to perform 'delete' actions
'actions'=>array('delete'),
'users'=>array('@'),
'expression'=>'isset($user->role) && ($user->role==1)',
),
array('deny',  // deny all users
'users'=>array('*'),
),
);
}
In this example only admin users and clerical users can perform the actions 'create','update', 'process', 'complete', 'progress', only clients (role = 3) can perform transfer and notice, and only admin can perform the delete action.

Using the 'expression' attribute in the array for the accessRules you can create very detailed and refined rules as to what actions are allowed to be executed depending on the users defined role.

References

  1. http://www.yiiframework.com/wiki/6/
  2. CBaseUserIdentity 

No comments: