Can't add an user to be an owner of more than one project?

I added everything according to the book and now just playing around with the application and noticed this. Our AuthAssignment table has itemname and userid as primary keys and when we execute action addUser if we add one user for example with id = 2 to two new projects as an "owner" the ProjectUserForm


public function verify($attribute,$params)

	{

		if(!$this->hasErrors()) // we only want to authenticate when no other input errors are present

		{

			$user = User::model()->findByAttributes(array('username'=>$this->username));

		if($this->project->isUserInProject($user))

		{

			$this->addError('username','This user has already been added to the project.');

		}

		else

		{

			$this->project->associateUserToProject($user);

			$this->project->associateUserToRole($this->role,$user->id);

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

			$bizRule='return isset($params["project"]) && $params["project"]->isUserInRole("'.$this->role.'");';

			$auth->assign($this->role,$user->id, $bizRule);

		}

		}

	}

will try to add two rows with the same userid and itemname in the AuthAssigment table and give this error:

CDbException

Description

CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘owner-7’ for key ‘PRIMARY’

Source File

C:\localhost\htdocs\yii\framework\db\CDbCommand.php(227)

00215: $n=$this->_statement->rowCount();

00216:

00217: if($this->_connection->enableProfiling)

00218: Yii::endProfile(‘system.db.CDbCommand.execute(’.$this->getText().’)’,‘system.db.CDbCommand.execute’);

00219:

00220: return $n;

00221: }

00222: catch(Exception $e)

00223: {

00224: if($this->_connection->enableProfiling)

00225: Yii::endProfile(‘system.db.CDbCommand.execute(’.$this->getText().’)’,‘system.db.CDbCommand.execute’);

00226: Yii::log('Error in executing SQL: '.$this->getText().$par,CLogger::LEVEL_ERROR,‘system.db.CDbCommand’);

00227: throw new CDbException(Yii::t(‘yii’,‘CDbCommand failed to execute the SQL statement: {error}’,

00228: array(’{error}’=>$e->getMessage())));

00229: }

00230: }

00231:

00232: /**

00233: * Executes the SQL statement and returns query result.

00234: * This method is for executing an SQL query that returns result set.

00235: * @param array input parameters (name=>value) for the SQL execution. This is an alternative

00236: * to {@link bindParam} and {@link bindValue}. If you have multiple input parameters, passing

00237: * them in this way can improve the performance. Note that you pass parameters in this way,

00238: * you cannot bind parameters or values using {@link bindParam} or {@link bindValue}, and vice versa.

00239: * binding methods and the input parameters this way can improve the performance.

Stack Trace

#0 C:\localhost\htdocs\yii\framework\web\auth\CDbAuthManager.php(281): CDbCommand->execute()

#1 C:\localhost\htdocs\trackstar\protected\models\ProjectUserForm.php(60): CDbAuthManager->assign(‘owner’, ‘7’, ‘return isset($p…’)

#2 C:\localhost\htdocs\yii\framework\validators\CInlineValidator.php(39): ProjectUserForm->verify(‘username’, Array)

#3 C:\localhost\htdocs\yii\framework\validators\CValidator.php(176): CInlineValidator->validateAttribute(Object(ProjectUserForm), ‘username’)

#4 C:\localhost\htdocs\yii\framework\base\CModel.php(150): CValidator->validate(Object(ProjectUserForm), NULL)

#5 C:\localhost\htdocs\trackstar\protected\controllers\ProjectController.php(227): CModel->validate()

#6 C:\localhost\htdocs\yii\framework\web\actions\CInlineAction.php(32): ProjectController->actionAdduser()

#7 C:\localhost\htdocs\yii\framework\web\CController.php(300): CInlineAction->run()

#8 C:\localhost\htdocs\yii\framework\web\filters\CFilterChain.php(129): CController->runAction(Object(CInlineAction))

#9 C:\localhost\htdocs\yii\framework\web\filters\CFilter.php(41): CFilterChain->run()

#10 C:\localhost\htdocs\yii\framework\web\CController.php(999): CFilter->filter(Object(CFilterChain))

#11 C:\localhost\htdocs\yii\framework\web\filters\CInlineFilter.php(59): CController->filterAccessControl(Object(CFilterChain))

#12 C:\localhost\htdocs\yii\framework\web\filters\CFilterChain.php(126): CInlineFilter->filter(Object(CFilterChain))

#13 C:\localhost\htdocs\yii\framework\web\CController.php(283): CFilterChain->run()

#14 C:\localhost\htdocs\yii\framework\web\CController.php(257): CController->runActionWithFilters(Object(CInlineAction), Array)

#15 C:\localhost\htdocs\yii\framework\web\CWebApplication.php(320): CController->run(‘adduser’)

#16 C:\localhost\htdocs\yii\framework\web\CWebApplication.php(120): CWebApplication->runController(‘project/adduser…’)

#17 C:\localhost\htdocs\yii\framework\base\CApplication.php(135): CWebApplication->processRequest()

#18 C:\localhost\htdocs\trackstar\index.php(12): CApplication->run()

#19 {main}

Did I make some mistake or is it overlooked in the book and what can we do with it for it to work like its intended?

You are correct in that this is only checking whether or not the user has been added to the project and not whether they have already been added in the same role on another project. All we need to do to address this is simply check if the user is already assigned to that role before we add them to the auth assignment table. The bizrule will actually take care of validating whether or not the role is applicable to the specific project. In other words, you could change this:


$bizRule='return isset($params["project"]) && $params["project"]->isUserInRole("'.$this->role.'");';  

$auth->assign($this->role,$user->id, $bizRule);

to this:


//check if this user is already in the auth hierarchy in this role, if so, we don't need to add them again as the bizrule will take care of validating the access within the project context

if(!$auth->isAssigned($this->role, $user->id))

{

 	$bizRule='return isset($params["project"]) && $params["project"]->isUserInRole("'.$this->role.'");';  

 	$auth->assign($this->role,$user->id, $bizRule);

}

thanks a lot, thought it would be the bizrule that maters but wasn’t sure so figured I best ask about it.