Role based authentication - need some help

Hi again,

  I am trying to implement role based authentication in my application without success. I have a PhpAuthManager file in my protected/components with contents - 

<?php

class PhpAuthManager extends CPhpAuthManager{

    public function init(){

       	$auth=Yii::app()->authManager;

		

		$changed = false;

		if ($auth->getAuthItem('showLocation') === NULL) {

				$auth->createOperation('showLocation','show locations');

				$changed = true;

		}

		if ($auth->getAuthItem('listLocation') === NULL) {

				$auth->createOperation('listLocation','list locations');

				$changed = true;

		}

		if ($auth->getAuthItem('createLocation') === NULL) {

				$auth->createOperation('createLocation','create locations');

				$changed = true;

		}

		if ($auth->getAuthItem('updateLocation') === NULL) {

				$auth->createOperation('updateLocation','update locations');

				$changed = true;

		}

		if ($auth->getAuthItem('loginSite') === NULL) {

				$auth->createOperation('loginSite','login action');

				$changed = true;

		}

		if ($auth->getAuthItem('reader') === NULL) {

				$role=$auth->createRole('reader');

				$role->addChild('listLocation');

				$role->addChild('showLocation');

				$changed = true;

		}

		if ($auth->getAuthItem('editor') === NULL) {

				$role=$auth->createRole('editor');

				$role->addChild('reader');

				$role->addChild('createLocation');

				$changed = true;

		}

		if($auth->getAuthItem('admin') === NULL) {

				$role=$auth->createRole('admin');

				$role->addChild('editor');

				$role->addChild('updateLocation');

				$changed = true;

		}

		if($auth->getAuthItem('guest') === NULL) {

				$role=$auth->createRole('guest');

				$role->addChild('loginSite');

				$changed = true;

		}

		if ($changed) {

				$auth->save();

		}

	}

	public function assignRole($role) {	

		$auth=Yii::app()->authManager;

		//var_dump($auth->roles);

		//var_dump($auth->isAssigned($role,Yii::app()->user->id));

		if (!$auth->isAssigned($role,Yii::app()->user->id)) {

				$auth->assign($role,Yii::app()->user->id);

				$changed = true;

		}

		if ($changed) {

				$auth->save();

		}

		//var_dump(Yii::app()->user->checkAccess('createLocation'));

	}

}

?>

My UserIdentity class is like this -


<?php


class UserIdentity extends CUserIdentity

{

	/**

	 * Authenticates a user.

	 * The example implementation makes sure if the username and password

	 * are both 'demo'.

	 * In practical applications, this should be changed to authenticate

	 * against some persistent user identity storage (e.g. database).

	 * @return boolean whether authentication succeeds.

	 */

	  protected $_id;

 

      public function authenticate(){

	  	

        $user = new users;

		$criteria=new CDbCriteria;

		$criteria->condition='username=:username';

		$criteria->params=array(':username'=>$this->username);

		$user = $user->find($criteria);

		

		if($user===null) {

            $this->errorCode = self::ERROR_USERNAME_INVALID;

        } else if($this->password !== $user->password ) {

			$this->errorCode = self::ERROR_PASSWORD_INVALID;

	} else {

            $this->_id = $user->id;

 

            $this->errorCode = self::ERROR_NONE;

	    [b]$phpauthmanager = new PhpAuthManager;

	    $phpauthmanager->init();

	    $phpauthmanager->assignRole($user->role);[/b]

	}

       return !$this->errorCode;

    }

 

    public function getId(){

        return $this->_id;

    }


}

?>

While authenticating the user I am retrieving the role of the user from the database and trying to assign to the user. I am checking the access in the location controller as - 

<?php

   if(Yii::app()->user->checkAccess('createLocation')) {

        //create the location 

   }

   else {

         //redirect to error page 

  }


?>

But this checkAccess is returning true sometime and false sometime for the same role if I logout and login again with the same role. Is there anything wrong in the code?

Another thing is if i want to access the application from multiple clients I think its mixing the roles. Doesn’t it keep sessions of each app user seperately?

Any help will be appriciated

Thanks!

I guess problem could be with assigning multiple roles to the same user. Your code contains only assign() method call for a user. What is done when user’s role is changed? Do you call revoke() in this case?

In my implementation I have following afterSave() method in the User model:




protected function afterSave()

    {

        $auth = Yii::app()->authManager;


        $items = $auth->getRoles($this->id);

        foreach ($items as $item) {

            $auth->revoke($item->name, $this->id);

        }

        

        $auth->assign($this->userType->name, $this->id);

        $auth->save();


        parent::afterSave();

    }



Hi seb thanks for the reply!

  Did you this method in user model which is used to create the users?. If yes, no I don't have this method in my user model. I don't get what this method does?. Can you please elaborate the code. 

I will explain what I want -

 I have a user table in that I am storing Username,Password,Email and Role. Role is having four possible values - guest,reader,editor and admin. You can see the allowed functions for these roles in PhpAuthManager class above. 


 When user logs in  I am trying to assign one of these  roles to the user depending on the Role value in the database for that user.


 But its not working? checkAccess function is not returning valid result depending on the role for different user logins. Can you please help me to get it working. One more thing is I want this application to be accessed by many people simultaneously and depending upon the Role they should get access to the features.

Thanks in advance!

I had a project recently where RBAC was used and there was very close to yours implementation. I had "student", "teacher", "school admin" and "superadmin" roles.

To build auth manager rules I used following method inside SiteController:




public function actionBuildAuth()

    {

        $auth=Yii::app()->authManager;


        $auth->clearAll();


        //create operations

        $op = $auth->createOperation('StudentPage','View Student\'s Page');        

        $op = $auth->createOperation('UsersViewSchool','Users - view School');

        $op = $auth->createOperation('UsersViewAll','Users - view all');

        $op->addChild('UsersViewSchool');

        $op = $auth->createOperation('UsersEditSchool','Users - Edit School');

        $op->addChild('UsersViewSchool');        

        $op = $auth->createOperation('UsersEditAll','Users - edit all');

        $op->addChild('UsersViewAll');

        $op->addChild('UsersEditSchool');


        ...


        //create roles and assign operations to roles

        $userTypes = UserType::model()->findAll();

        foreach($userTypes as $userType) {

            $role = $auth->createRole($userType->name);

            if ($userType->name == 'student') {

                $role->addChild('StudentPage');

            }

            if ($userType->name == 'teacher') {

                $role->addChild('UsersViewSchool');

                $role->addChild('ClassesViewSchool');

                ...  

            }

            if ($userType->name == 'admin') {

                $role->addChild('UsersViewSchool');

                $role->addChild('UsersEditSchool');

                ...

            }

        }



I invoked this method manually to make initial auth rules setup.

Now if new user is created or if we change role for existing user we need to reassign role. I do this inside User::afterSave() method:




protected function afterSave()

    {

        $auth = Yii::app()->authManager;

        

        // revoke all auth items assigned to the user

        $items = $auth->getRoles($this->id);

        foreach ($items as $item) {

            $auth->revoke($item->name, $this->id);

        }

        

        // assign new role to the user

        $auth->assign($this->userType->name, $this->id);

        $auth->save();


        parent::afterSave();

    }



In my case auth rules for the user are updated automatically when user data is saved. I not checking here if role is changed, just revoke all items from the user and assign new role.

In your case I think it is OK to assign role when user logging in, but if user role is changed since last time then many roles can be assigned to the user. I think this can be the reason of incorrect checkAccess behavior (for example you can have the user with reader and admin roles assigned simultaneously.

So you can try to change your method like this:




public function assignRole($role) {     

        // revoke all auth items assigned to the user

        $items = $auth->getRoles(Yii::app()->user->id);

        foreach ($items as $item) {

            $auth->revoke($item->name, Yii::app()->user->id);

        }

        

        // assign new role to the user

        $auth->assign($role, Yii::app()->user->id);

        $auth->save();

        }



Thanks again for the reply!

   I think I have discovered the problem. I am not getting the value for   - 

Yii::app()->user->id

in PhpAuthManager class.

    I think I need to do something with CWebUser. If you have any idea about why this is happening? Please let me know.

Thanks!

What does it mean "not getting value"? Does user->id is always 0 (as for guest user)?

I’d suggest you to use debugger and go step by step through your and yii’s code to understand what is going on and why you not getting value for user->id. I am personally using NetBeans + xdebug php extension for debugging and this is one of the best methods to learn how Yii works.

I looked now one more time your code and think you should not create PhpAuthManager instance inside authenticate() method. Auth manager is application component and you need to configure it from config/main.php file like this:




    // application components

    'components'=>array(

        ...

        'authManager'=>array(

            //your subclass here

            'class'=>'PhpAuthManager',

            

        ),



During application initialization your method PhpAuthManager::init() should be invoked automatically and then you need to invoke assignRole() after user is logged in:




    public function authenticate(){

                

        $user = new users;

        ...

                

        if($user===null) {

            $this->errorCode = self::ERROR_USERNAME_INVALID;

        } else if($this->password !== $user->password ) {

                        $this->errorCode = self::ERROR_PASSWORD_INVALID;

        } else {

            $this->_id = $user->id;

 

            $this->errorCode = self::ERROR_NONE;

            //assign role          

            Yii::app()->authManager->assignRole($user->role);

        }

       return !$this->errorCode;

    }



Maybe there should be some step-by-step tutorial about RBAC? Can someone with RBAC experience post this?

Hi seb you are very helpful!

   If I configure my application as 



     // application components

    'components'=>array(

        ...

        'authManager'=>array(

            //your subclass here

            'class'=>'PhpAuthManager',

            

        ),

And I am not creating instance of my PhpAuthManager in authenticate() method and calling assignRole as you said like - 

   Yii::app()->authManager->assignRole($user->role);

 I am getting error like - 
  But I tried by configuring the my components like - 

 

                'authManager' => array(

			'class'=>'CPhpAuthManager',

			'defaultRoles' => array('guest'),

		),

		'myAuthManager' => array(

			'class'=>'PhpAuthManager',

		),

  And calling assignRole() method as - 

Yii::app()->myAuthManager->assignRole($user->role);

  RBAc is working fine for me&#33;

Thanks again for helping me to get it working!

Now I want something else in RoleBased Authentication,

   I don't want to save the AuthItems to persistent storage. I want to generate assign operation to the user role while logging the user in the application depending upon his access criteria. 


  So my PhpAuthManager looks like - 



<?php

class PhpAuthManager extends CPhpAuthManager{

	public function init(){

		$auth=Yii::app()->authManager;

		$auth->createOperation('mainSite','Main Action');

                $role=$auth->createRole('admin');

	        $role->addChild('mainSite');

        }

        public function assignRole() {	

		$auth=Yii::app()->authManager;

                $auth->assign('admin', Yii::app()->user->id);

                #var_dump(Yii::app()->user->checkAccess('mainSite'));

        }

?>



In my site controller I am checking the access as -




function actionMain() {

    if(Yii::app()->user->checkAccess('mainSite')) {

         #my code 

    }

    else {

        #var_dump(Yii::app()->user->checkAccess('mainSite'));

	Yii::app()->request->redirect(Yii::app()->user->returnUrl);

    }

} 



In my site controller I am not getting access to Main action. If I dump var_dump(Yii::app()->user->checkAccess(‘mainSite’)) in my assignRole function its returning true. But If I am dumping this var in actionMain, then its returning false.

Anybody please help me in doing this.

Thanks!