Jay - thanks so much, your answer was spot on. The solution below works.
Again, what this "backdoor" procedure does is to grant an authenticated user in the "admin" module access – to ANY account desired – in the "portal" module.
See my questions at the bottom–would love advice from security experts.
Steps involved:
// 1. AdminModule::init(). Set stateKeyPrefix to 'admin':
public function init() {
// import the module-level models and components
$this->setImport(array(
'admin.models.*',
'admin.components.*',
));
Yii::app()->setComponents(array(
'user'=>array(
'class'=>'CWebUser',
'stateKeyPrefix'=>'admin',
'loginUrl'=>Yii::app()->createUrl('admin/default/login'),
'returnUrl'=>Yii::app()->createUrl('admin/'),
)));
}
// -----------------------------------------------------------------------
// 2. Modify the portal authentication method -- UserIdentity.php::init()
// -----------------------------------------------------------------------
/**
* Authenticates the user with name given by $this->username -- or grants backdoor access.
* @param $backdoorAs - string - If non-null, then "Authenticate" as the portal user
* with the name $backdoorAs. I.e., we won't truly authenticate, we'll
* instead grant access as long as the currently logged-in user is
* logged in to *admin* module.
* @return boolean whether authentication succeeds.
*
* NOTE: backdoor authentication will NEVER work if you logged in using the actual
* login process. LOG OUT of the portal first.
*/
public function authenticate($backdoorAs=null)
{
$this->errorCode=self::ERROR_NONE;
$backdoor = $backdoorAs !== null; // for readability
if ($backdoor) {
$this->username = $backdoorAs;
}
$user=User::model()->find('LOWER(username)=?',array(strtolower($this->username)));
if($user===null)
$this->errorCode=self::ERROR_USERNAME_INVALID;
else {
// Backdoor authentication. Allow login if admin session var 'admin__id'
// is non-zero. (Session keys are prepended with the string 'admin' because
// that's how we set up the stateKeyPrefix in AdminModule.php, above.)
if ($backdoor) {
$adminId = Yii::app()->session->itemAt('admin__id');
if ($adminId === null || $adminId < 1) {
$this->errorCode=self::ERROR_USERNAME_INVALID;
}
}
// Else, the usual portal login, validating password
else if (!$user->validatePassword($this->password))
$this->errorCode=self::ERROR_PASSWORD_INVALID;
}
// User is authentic.
if ($this->errorCode == self::ERROR_NONE) {
$this->_id=$user->id;
$this->username=$user->username;
}
return $this->errorCode==self::ERROR_NONE;
}
// -----------------------------------------------------------------------
// 3. Create a controller for the "portal" module:
// -----------------------------------------------------------------------
/**
* Controller for backdooring in, from admin, to the portal.
*/
class BackdoorController extends Controller
{
/**
* If the current user is an admin user, who has successfully authenticated, and if
* the variable "user" is present as part of the request, then allow this user to view the portal,
* as if logged in as the user specified in the query string.
*/
public function actionIndex()
{
$user= Yii::app()->request->getQuery('user', '');
// If no user, or empty user, quietly redirect.
if (empty($cpanelUser)) {
$this->redirect(Yii::app()->createUrl('portal/'));
return;
}
$password = '';
$identity=new UserIdentity($user, $password);
if ($identity->authenticate($user)) {
$duration= 2592000; // 3600*24*30 = 30 days
Yii::app()->user->login($identity, $duration); // BACKDOOR
}
$this->redirect(Yii::app()->createUrl('portal/'));
} else {
// Authentication failed. Quietly redirect.
$this->redirect(Yii::app()->createUrl('portal/'));
}
}
}
// -----------------------------------------------------------------------
// 4. Create links to the backdoor, in views, etc., in the "admin" module
// -----------------------------------------------------------------------
echo CHtml::link('login as sue',
Yii::app()->createUrl('portal/backdoor', array('user'=>'sue')));
If anyone can add some measures of SECURITY to this implementation, I would appreciate it. My worry, specifically, is that a portal user could guess the backdoor URL :
index.php/portal/backdoor/user/sue
If they were to guess that URL, would it be possible for them to fake the admin__id SESSION variable such that they could gain access to sue’s portal? If so, what can I do to make this more secure?
