Yii 1.1: Yet another implementation of CPhpAuthManager.

7 followers

In this wiki, I try to implement a simple authorization schema without putting much logic inside a file or into database table. We are constructing authorization hierarchy inside the controller. We are getting roles for the current user from database table and assigning only roles to user that are declared in the particular controller. We have brought down the work of loading of auth data at main application level to controller level. This way we have pulverised auth data for entire site into smaller units. Finally we are going to look at couple of examples.

1. Induct the component into the application.

protected/config/main.php

'components'=>array(
        'authManager'=>array(
            'class'=>'CPhpAuthManager',
            ),

2. Keep the auth.php empty or void.

By default if authorization file is not declared in main configuration file, CPhpAuthManager file will look for a file protected/data/auth.php. Now we have to either delete the file or keep an empty array inside it.

protected/data/auth.php

return array();

3. Keep the roles inside the database.

The important thing is to declare the roles for individual user and store them inside the database table. In simpler situations, we can store the roles inside the user table itself. For following examples, I have created a separate table called role and created some roles.

  • PostManagement:reader,author,editor,chiefEditor.
  • UserManagement:userManager.

4. Assign the user a unique id.

By making some changes in the UserIdentity file in components folder, we can assign a unique id for individual user. Now Yii::app()->user->id would fetch the unique id.

protected/components/UserIdentity.php

class UserIdentity extends CUserIdentity
{   
    private  $_id;
    public function authenticate()
    {
        $user=User::model()->findByAttributes(array('username'=>$this->username));
        if($user===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        elseif($user->password!==md5($this->password."someSalt"))
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
        {   
            $this->errorCode=self::ERROR_NONE;
            $this->_id=$user->id; //Here we are assigning pk of $user as user ID.
        }
        return !$this->errorCode;
    }
 
    public function getId()
    {
        return $this->_id;
    }
}

5. Construct the authorization hierarchy inside the controller.

We are going to declare a method inside the PostContoller.php.

PostController::loadAuth()

protected function loadAuth()
    {
        $auth=Yii::app()->authManager;
 
        //We have finely ground the permissions to controll all the actions.
        $auth->createOperation('viewPostList','view the list of posts');
        $auth->createOperation('viewPost','view a post');
        $auth->createOperation('createPost','create a new post');
        $auth->createOperation('updatePost','update a post');
        $auth->createOperation('deletePost','delete a post');
        $auth->createOperation('managePost','administer publications');
 
               /**We have created a role  'reader'.
                * Reader can view the list of posts or view a single post.
                */ 
        $reader=$auth->createRole('reader');
        $reader->addChild('viewPostList');
        $reader->addChild('viewPost');
 
        /**We are going to create a role  'author'. 
         * Author can be a reader.
         * Author can also create a post
         * Author will get a task 'updateOwnPost'.
         * Through this task, author can only update his/her own post.
         * For this purpose, we have assinged a rule for it.
         */ 
        $bizRule='return Yii::app()->user->id==$params["post"]->author_id;';
        $task=$auth->createTask('updateOwnPost','update only posts created by user',$bizRule);
        $task->addChild('updatePost');
 
        $author=$auth->createRole('author');
        $author->addChild('createPost');
        $author->addChild('updateOwnPost');
        $author->addChild('reader');
 
        /**We have created another role  'editor'.
         * Editor is a reader.
         * He can edit any post.
         * But he can not edit posts made by chiefEditor.
         * To achieve this we have created a task with a biz rule.
         */
        $bizRule='return (int)$params["post"]->author_id!==1;';  //1==admin
        $task=$auth->createTask('updateNotChiefEditorPost','update only posts created by authors',$bizRule);
        $task->addChild('updatePost');
 
        $editor=$auth->createRole('editor');
        $editor->addChild('updateNotChiefEditorPost');
        $editor->addChild('reader');
 
        // 'ChiefEditor'  has got all the rights.
        $chiefEditor=$auth->createRole('chiefEditor');
        $chiefEditor->addChild('reader');
        $chiefEditor->addChild('createPost');
        $chiefEditor->addChild('updatePost');
        $chiefEditor->addChild('deletePost');
        $chiefEditor->addChild('managePost');
 
        return $auth;
    }

6. Selectively assign roles to the user.

Now we are going to fetch all the roles of current user from the database and going to assign the roles selectively.

PostController::assignRoles()

protected function assignRoles()
    {
        $roles=Yii::app()->db->createCommand()
            ->select('role_name')
            ->from('role')
            ->where('user_id=:id',array(':id'=>Yii::app()->user->id))
            ->queryColumn();
 
        $auth=$this->loadAuth();
 
        foreach($roles as $role)
        {   
            /*We are not going to assign all the roles.
            *Only roles pertinent to this controller are assigned.
            */
            if($auth->getAuthItem($role)!==null)
                $auth->assign($role,Yii::app()->user->id);
        }
    }
 
//Now call this method inside PostController::init()
public function init()
    {
        $this->assignRoles();   
    }

7. Declare the rules in accessRules.

Now we are going to infuse this authorization logic into CAccessController filter.

PostController::accessRules()

public function accessRules()
    {   
        /**We have some business rules related to updating a paricular post.
         * To put that paricular post inside the params, we need  to know the pk value of that post prior hand.
         * We can do the following to achieve that.
         */
        $params=array();
        $id=Yii::app()->request->getParam('id');//Choose the GET/POST parameters appropriately and populate $params.
        if(isset($id))
            $params['post']=$this->loadModel($id);
 
        /**We assign only the basic operations for each rule here.
         * CPhpAuthManager::checkAccess() method will take care of parents(task,role)
         * Also look into code of CAccessRule::isRoleMatched() method.
         */     
 
        return array(
            array('allow',  
                'actions'=>array('index'),
                'roles'=>array('viewPostList'),
            ),
            array('allow',  
                'actions'=>array('view'),
                'roles'=>array('viewPost'),
            ),
            array('allow', 
                'actions'=>array('create'),
                'roles'=>array('createPost'),
            ),
            array('allow', 
                'actions'=>array('update'),
                'roles'=>array('updatePost'=>$params),
            ),
            array('allow', 
                'actions'=>array('delete'),
                'roles'=>array('deletePost'),
            ),
            array('allow', 
                'actions'=>array('admin'),
                'roles'=>array('managePost'),
            ),
            array('deny', 
                'users'=>array('*'),
            ),
        );
    }

Now in the same application, we are building authorization scheme for userManagement.

UserController.php

protected function loadAuth()
    {
        $auth=Yii::app()->authManager;
 
        /**Here we are declaring default roles.
         * Default roles are assigned to all the users.
         * It is going to work, if  assigned bizrules are met.
         */ 
        $auth->defaultRoles=array('anonymous','authenticated');
 
 
        $auth->createOperation('listAccounts','view the list of all users');
        $auth->createOperation('viewAccount','view an user account');
        $auth->createOperation('register','register an account');
        $auth->createOperation('updateAccount','update an account');
        $auth->createOperation('deleteAccount','delete an user account');
        $auth->createOperation('manageAccount','administer user accounts');
 
        /**Default role  'anonymous'  is created.
         * We are attaching a bizRule so that guests only can assume anonymous role.
         * They can only create an account.
         */ 
        $bizRule='return Yii::app()->user->isGuest;';
        $anonymous=$auth->createRole('anonymous','anonymous only can register',$bizRule);
        $anonymous->addChild('register');
 
        /**Default role  'authenticated'  is created.
         * This has a child  'userAccount' (task).
         * The task ensures that user can view or update only his or her account.
         */
        $bizRule='return Yii::app()->user->id==$params["user"]->id;';
        $task=$auth->createTask('userAccount','view or edit only own account',$bizRule);
        $task->addChild('viewAccount');
        $task->addChild('updateAccount');
 
        $authenticated=$auth->createRole('authenticated');
        $authenticated->addChild('userAccount');
 
        /** 'userManger'  role is declared in database.
         * He has all the rights regarding user accounts.
         */ 
        $manager=$auth->createRole('userManager');
        $manager->addChild('listAccounts');
        $manager->addChild('viewAccount');
        $manager->addChild('updateAccount');
        $manager->addChild('userAccount');
        $manager->addChild('deleteAccount');
        $manager->addChild('manageAccount');
 
        return $auth;
    }
 
public function accessRules()
    {   
        $params=array();
        $id=Yii::app()->request->getParam('id'); //Choose the GET/POST parameters appropriately and populate $params.
        if(isset($id))
            $params['user']=$this->loadModel($id);
 
                /* *The parent task  'userAccount'  has bizRule with it.    
                    *So we have to pass params with updateAccount and ViewAccount.
                    */
        return array(
            array('allow',  
                'actions'=>array('index'),
                'roles'=>array('listAccounts'),
            ),
            array('allow',  
                'actions'=>array('view'),
                'roles'=>array('viewAccount'=>$params),
            ),
            array('allow', 
                'actions'=>array('register'),
                'roles'=>array('register'),
            ),
            array('allow', 
                'actions'=>array('update'),
                'roles'=>array('updateAccount'=>$params),
            ),
            array('allow', 
                'actions'=>array('delete'),
                'roles'=>array('deleteAccount'),
            ),
            array('allow', 
                'actions'=>array('admin'),
                'roles'=>array('manageAccount'),
            ),
            array('deny', 
                'users'=>array('*'),
            ),
        );
    }
/**You have to declare the method assignRoles here also .
*You have to call it  inside UserController::init() method.
*/

I really thank everyone going through this wiki. I would gracefully accept any criticism.

Thanking again!.

Total 3 comments

#16693 report it
codesutra at 2014/03/20 03:07am
Loading of auth data evrytime

Hey, first of all thanks for the nice article written by you. I have once question in my mind regarding this article.i hope you will answer it.:)

As we can see you are calling assignRoles() method in init fucntion of the controller.

//Now call this method inside PostController::init()
public function init()
    {
        $this->assignRoles();   
    }

SO, don't you think all this permission will be created everytime.whenever we will call a request to PostController ??

Looking forward to hear from you.

#12839 report it
shiv at 2013/04/15 11:07am
Good Article ! Thanks

Good Article ! Thanks

#12832 report it
PeRoChAk at 2013/04/15 03:52am
Thanks

Very nice

Leave a comment

Please to leave your comment.

Write new article