Different Users Using Same Model Security

Hi,

This is a long post, but the question is very simple. How to prevent users from updating fields there not allow to?

I’m using the accessRules and user Roles, works awesome.

Now I wonder, I got two forms using the same model. One form for the admin, one for the user.

Simplefied: in the user form I got: title and description.

When saved, the actionUserUpdate is used like this:





	public function actionUserUpdate($id)

	{

		$model=$this->loadModel($id);

		

		if(do some owner checks here)

		{

			throw new CHttpException(403,'No access');

		}

		else

		{			

			if(isset($_POST['ModelTest']))

			{

				$model->attributes=$_POST['ModelTest'];

				if($model->save())

				{

// etc...




But in the admin form I also got the field ‘extra_info’. And the admin is using the actionAdminUpdate action, all fine.

But if a user is a smartass and modify the html of the form, and if he guess the right column ‘extra_info’ and make an html input in the code, then it would be stored if hey submitted it right?

How do you handle this? Or is it the only way to set only the allowed columns like this:





	public function actionUserUpdate($id)

	{

		$model=$this->loadModel($id);

		

		if(do some owner checks here)

		{

			throw new CHttpException(403,'No access');

		}

		else

		{			

			if(isset($_POST['ModelTest']))

			{

				$model->title=$_POST['ModelTest']['title'];

				$model->description=$_POST['ModelTest']['description'];

				if($model->save())

				{

// etc...




Of course this works, but if you have like 14 columns, it looks kinda dirty to me.

What is a good and secure way to handle this?

Scenarios.

Thanks, I use scenarios a lot, but did not know I could use it for this.

So if i’m correct, adding this rule would be the right way to make it safe for example?




	public function rules()

	{

		return array(

			array('extra_info', 'unsafe', 'on'=>'usercreate,userupdate'),

		);

	}



Because this would not help right?




	public function rules()

	{

		return array(

			array('extra_info', 'safe', 'on'=>'admincreate,adminupdate'),

		);

	}



Hi!

I think try the inheritance - so there will be two models: User and AdminUser - for example:




class User extends CActiveRecord {

    

    public function tableName() {

        return 'tbl_user';

    }

    

    public function rules() {

        return array(

            array('username, password, email', 'required'),

            array('username, password, email', 'length', 'max' => 128)

        );

    }

    

    public function relations() {

        return array();

    }

    

    public function attributeLabels() {

        return array(

            'id' => 'ID',

            'username' => 'Username',

            'password' => 'Password',

            'email' => 'Email'

        );

    }

    

    public function search() {

        // ......

    }

    

    public static function model($className = __CLASS__) {

        return parent::model($className);

    }

    

    protected function instantiate($attributes) {

        if (!empty($attributes['is_admin'])) {

            $model = new AdminUser(null);

        } else {

            $model = new User(null);

        }

        return $model;

    }

}



and




class AdminUser extends User {

    

    public function rules() {

        return CMap::mergeArray(

            parent::rules(),

            array(

                array('extra_field', 'safe', 'on' => 'create, update')

            )

        );

    }

    

    public function beforeSave() {

        if( $this->isNewRecord ) {

            $this->is_admin = true;

        }

        return true;

    }

    

    public function attributeLabels() {

        return CMap::mergeArray(

            parent::attributeLabels(),

            array(

                'extra_field' => 'Extra Field'

            )

        );

    }

    

    public static function model($className = __CLASS__) {

        return parent::model($className);

    }

}



when you load the model the instantiate() method handle the two class - for example:




/*

id  username    password    email                 extra_field is_admin

1   admin       ...         email1@example.com    something   1

2   user        ...         email2@example.com    NULL        0

*/


$user = User::model()->findByPk(1);

echo get_class($user);                 // -> AdminUser

$user->attributes = array(

    'extra_field' => 'something_1', 

    'email' => 'newemail_1@example.com'

);

$user->save();

var_dump($user->id);                   // -> "1"

var_dump($user->email);                // -> "newemail_1@example.com"

var_dump($user->extra_field);          // -> "something_1"


$user = User::model()->findByPk(2);

echo get_class($user);                 // -> User

$user->attributes = array(

    'extra_field' => 'something_2', 

    'email' => 'newemail_2@example.com'

);

$user->save();

var_dump($user->id);                   // -> "2"

var_dump($user->email);                // -> "newemail_2@example.com"

var_dump($user->extra_field) ;         // -> NULL



Wow, that seems to be a cleaver way to do this. Thanks a lot I can use this for many more issues! :D