Implementing a User Level Access System

32 followers

Introduction

I would like to provide you a quick tip on how to implement user level access to your Yii applications.

Please note that this article is a simple example and good security should be taken into account when we play with authentication systems.

Step 1. Setting up

a. Include a field on your user’s table named, yep you guessed, level
b. Create an object ‘LevelLookUp’ that will tell us who is who on this system

class LevelLookUp{
      const MEMBER = 0;
      const ADMIN  = 2;
      // For CGridView, CListView Purposes
      public static function getLabel( $level ){
          if($level == self::MEMBER)
             return 'Member';
          if($level == self::ADMIN)
             return 'Administrator';
          return false;
      }
      // for dropdown lists purposes
      public static function getLevelList(){
          return array(
                 self::MEMBER=>'Member',
                 self::ADMIN=>'Administrator');
    }
}

c. Now, lets modify UserIdentity, located on your protected/components (normally by default), so we can store the user Model id on successful authentication, as CWebUser’s id property is set to the user Model’s username by default.

class UserIdentity extends CUserIdentity
{
    private $_id;
    /**
     * Authenticates a user.
     * @return boolean whether authentication succeeds.
     */
    public function authenticate()
    {
        $username = strtolower($this->username);
        // from database... change to suite your authentication criteria
        // -- Nope, I wont include mine --
        $user = User::model()->find('LOWER(username)=?', array($username));
        if($user===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if(!$user->validatePassword($this->password))
            $this->errorCode = self::ERROR_PASSWORD_INVALID;
        else{
            // successful login
            $this->_id = $user->id;
            $this->username = $user->username;
            $this->errorCode = self::ERROR_NONE;
        }
        return $this->errorCode == self::ERROR_NONE;
    }
    public function getId()
    {
        return $this->_id;
    }
}

d. Now we need to create our own extended CWebUser and we are going to call it EWebUser for the sake of this example, and save it on protected/components to be loaded automatically by our default’s configuration file.

class EWebUser extends CWebUser{
 
    protected $_model;
 
    function isAdmin(){
        $user = $this->loadUser();
        if ($user)
           return $user->level==LevelLookUp::ADMIN;
        return false;
    }
 
    // Load user model.
    protected function loadUser()
    {
        if ( $this->_model === null ) {
                $this->_model = User::model()->findByPk( $this->id );
        }
        return $this->_model;
    }
}

e. Finally, we modify main.php config file (this file is in protected/config folder) to tell Yii that we want to use our extended version of CWebUser.

// go to the 'user' section
// application components
    'components'=>array(
        'user'=>array(
            // There you go, use our 'extended' version
            'class'=>'application.components.EWebUser',
            // enable cookie-based authentication
            'allowAutoLogin'=>true,
        ),

Step 2: Putting everything together

Now that we know, who logged, we could easily find out if it is just a member or an administrator and render the elements by checking the level as simple as this:

// for normal content
if(Yii::app()->user->isAdmin())
     echo 'Is administrator';
 
// for CMenus
$this->widget('zii.widgets.CMenu',array(
    array('label'=>'Categories',
           'url'=>array('/category/index'),
           'visible'=>(Yii::app()->user->isAdmin()),
     //... More stuff
     //...
 
// for data chuncks
<?php if(Yii::app()->user->isAdmin():?>
<b>My HTML</b>
<?php endif;?>
 
// for access rules
return array(
      array('allow', 
        'actions'=>array('create','delete','update'),
        'expression'=>'$user->isAdmin()'
      ),
// ...

You can create more methods, like for example, isMember(), or whatever best suits you, in order to display the appropriate view or specific data according to the user level.

Total 7 comments

#7279 report it
ragua at 2012/03/09 06:11am
Thank you

Thank you for these explanations. It works perfectly, i just don't understand the way to use LevelLookUp in gridview and listview of user's grids. Could you explain more?

#6048 report it
malcolmyam at 2011/12/08 02:34am
thanks!

simple n effective.. short n fast way to get a access system up

#3961 report it
RusAlex at 2011/05/24 02:04am
nice article

Really easy !

#3837 report it
tonydspaniard at 2011/05/13 01:46am
@derelict

Thank you again derelict... Code modified...

#3829 report it
derelict at 2011/05/12 06:57am
User's ID

You have override CUserIdentity class and now it's getter getId() returns user's ID, not username. That's why in EWebUser class getter getId() will return user's ID too.

#3828 report it
tonydspaniard at 2011/05/12 06:37am
@derelic

Thanks for pointing me the little bug! I forgot to remove that.

About using setState and getState I used to keep the model's id instead of the username as it internally does, and didn't want to modify CWebUser defaults, that's why I used another variable name (__uid)

Thanks again!

#3824 report it
derelict at 2011/05/11 05:53pm
Thank you

Thank you for your article. But I think you have error in this line of EWebUser class:

if ( $id !== null )

And why do you use setState/getState? I think that CWebUser stores id itself using this methods.

Leave a comment

Please to leave your comment.