Yii Framework Forum: Idea for auth - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Idea for auth Rate Topic: -----

#1 User is offline   jayrulez 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 348
  • Joined: 29-July 09

Posted 07 October 2009 - 02:20 PM

What do you think about changing the instances of 'username' in the auth component to something more generic like 'account'. Username just seems so specific. like i use my username 'jayrulez' to log on to this forum, but not all applications use username for authentication but email, phone #s etc... using account wouldn't be as specific as username as account can be represented by id, username, email and much more. How about this for Yii?
php:
foreach(array('cat', 'dog', 'cow') as $animal) echo $animal."\n";

python:
[(animal, print(animal)) for animal in ['cat', 'dog', 'cow']]

ruby:
['cat', 'dog', 'cow'].each {|animal| puts animal}


You say Tomato, I say Tomato.
0

#2 User is offline   phpdevmd 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 73
  • Joined: 16-April 09

Posted 08 October 2009 - 03:54 AM

In Web3CMS we thought about we same, and created a way to log in with either "Username" or "Email" or "Username or email". This is configured in params.php with
// which field to log user in with. possible values: username/email/_any_
    // reset this on any page with MParams::setUserLoginWithField('username');
    'userLoginWithField'=>'username', //'username'

Then in login view we add activeHiddenField($form,'loginWithField') to understand what is the account field, and activeTextField($form,$form->getLoginWithField()) to display the account field itself.
Then in UserLoginModel we read the account field and pass it to _CUserIdentity component, which is validating user input agains User db-table according to the account field.
Web3CMS is open source, feel free to download it and try (link is in the bottom).

Listing of user login view:
<?php MParams::setPageLabel(Yii::t('page','Login')); ?>
<?php MUserFlash::setTopError(_CHtml::errorSummary($form)); ?>
<?php MUserFlash::setSidebarInfo(Yii::t('feedback','Hint: You may login with <tt>demo/demo</tt> or <tt>admin/admin</tt>.')); ?>
<?php $this->widget('application.components.WContentHeader',array(
    'breadcrumbs'=>array(
        array(
            'url'=>CHtml::normalizeUrl(array($this->getAction()->getId())),
            'active'=>true
        ),
    ),
)); ?>
<div class="w3-main-form-wrapper ui-widget-content ui-corner-all">

<?php echo _CHtml::beginForm('','post',array('class'=>'w3-main-form'))."\n"; ?>
<?php echo _CHtml::activeHiddenField($form,'loginWithField')."\n"; ?>

<div class="w3-form-row w3-first">
  <div class="w3-form-row-label"><?php echo _CHtml::activeLabelEx($form,$form->getLoginWithField()); ?></div>
  <div class="w3-form-row-input">
    <?php echo _CHtml::activeTextField($form,$form->getLoginWithField(),array('class'=>'w3-input-text ui-widget-content ui-corner-all'))."\n"; ?>
  </div>
  <div class="clear">&nbsp;</div>
</div>
<div class="w3-form-row">
  <div class="w3-form-row-label"><?php echo _CHtml::activeLabelEx($form,'password'); ?></div>
  <div class="w3-form-row-input">
    <?php echo _CHtml::activePasswordField($form,'password',array('class'=>'w3-input-text ui-widget-content ui-corner-all','maxlength'=>64))."\n"; ?>
  </div>
  <div class="clear">&nbsp;</div>
</div>
<?php if(Yii::app()->user->allowAutoLogin): ?>
<div class="w3-form-row">
  <div class="w3-form-row-label">&nbsp;</div>
  <div class="w3-form-row-input">
    <div class="w3-form-row-text">
      <?php echo _CHtml::activeCheckBox($form,'rememberMe')."\n"; ?>
      <?php echo _CHtml::activeLabelEx($form,'rememberMe')."\n"; ?>
    </div>
  </div>
  <div class="clear">&nbsp;</div>
</div>
<?php endif; ?>
<div class="w3-form-row">
  <div class="w3-form-row-label">&nbsp;</div>
  <div class="w3-form-row-input">
    <?php echo _CHtml::submitButton(Yii::t('link','Log in'),array('class'=>'w3-input-button ui-button ui-state-default ui-corner-all'))."\n"; ?>
  </div>
  <div class="clear">&nbsp;</div>
</div>

<?php echo _CHtml::endForm(); ?>

</div><!-- w3-main-form-wrapper -->

<?php MClientScript::registerScript('focusOnFormFirstItem'); ?>
<?php MClientScript::registerScript('w3FormButton'); ?>


Listing of UserLoginForm model:
<?php

/**
 * UserLoginForm class.
 * UserLoginForm is the data structure for keeping
 * user login form data. It is used by the 'login' action of 'UserController'.
 */
class UserLoginForm extends CFormModel
{
    public $email;
    public $password;
    public $rememberMe;
    public $username;
    public $usernameOrEmail;
    public static $loginWithField;

    public function __construct($attributes=array(),$scenario='')
    {
        parent::__construct($attributes,$scenario);
        self::$loginWithField=$this->getLoginWithField();
    }

    /**
     * Declares the validation rules.
     * The rules state that username and password are required,
     * and password needs to be authenticated.
     */
    public function rules()
    {
        return array(
            // username or email or usernameOrEmail is required
            array(self::getLoggingWithField(), 'required'),
            // password is required
            array('password', 'required'),
            // password needs to be authenticated
            array('password', 'authenticate'),
        );
    }

    /**
     * Declares the attribute labels.
     * If an attribute is not delcared here, it will use the default label
     * generation algorithm to get its label.
     */
    public function attributeLabels()
    {
        return array(
            'rememberMe'=>Yii::t('feedback','Remember my member account on this computer'),
            'password'=>Yii::t('t','Password'),
            'username'=>Yii::t('t','Username'),
            'usernameOrEmail'=>Yii::t('t','Username or email'),
        );
    }

    /**
     * Authenticates the password.
     * This is the 'authenticate' validator as declared in rules().
     */
    public function authenticate($attribute,$params)
    {
        if(!$this->hasErrors())  // we only want to authenticate when no input errors
        {
            $identity=new _CUserIdentity($this->{self::getLoggingWithField()},$this->password);
            $identity->authenticate();
            switch($identity->errorCode)
            {
                case _CUserIdentity::ERROR_NONE:
                    // if user is already logged in
                    if(!Yii::app()->user->isGuest)
                    {
                        // log user out from the current account. i want to sleep well, do you? ;)
                        Yii::app()->user->logout();
                        if(!Yii::app()->getSession()->getIsStarted())
                            // restore http session. this is necessary for login
                            Yii::app()->getSession()->open();
                    }
                    // remember for 30 days. makes sence only if auto-login is allowed
                    $duration=(Yii::app()->user->allowAutoLogin && $this->rememberMe) ? 3600*24*30 : 0;
                    // log user in and save in session all appended data
                    Yii::app()->user->login($identity,$duration);
                    // set user preferences (for welcome message, and so on)
                    if(isset(Yii::app()->user->interface) && !empty(Yii::app()->user->interface))
                        // set user preferred interface
                        W3::setInterface(Yii::app()->user->interface);
                    if(isset(Yii::app()->user->language) && !empty(Yii::app()->user->language))
                        // set user preferred language
                        W3::setLanguage(Yii::app()->user->language);
                    break;
                case _CUserIdentity::ERROR_USERNAME_INVALID:
                    if(self::getLoggingWithField()==='username')
                        $this->addError('username',Yii::t('t','Username is incorrect.'));
                    else if(self::getLoggingWithField()==='email')
                        $this->addError('email',Yii::t('t','Email is incorrect.'));
                    else if(self::getLoggingWithField()==='usernameOrEmail')
                        $this->addError('usernameOrEmail',Yii::t('t','Username or email is incorrect.'));
                    break;
                case _CUserIdentity::ERROR_ACCOUNT_IS_INACTIVE:
                    // set the error message
                    MUserFlash::setTopError(Yii::t('feedback',
                        'We are sorry, but your member account is marked as "inactive". Inactive member accounts are temporarely inaccessible. {contactLink}.',
                        array('{contactLink}'=>CHtml::link(Yii::t('link','Contact us'),array('site/contact')))
                    ));
                    // add to username (first field in the login form) error css class
                    // and make the validate() to fail
                    $attribute=self::getLoggingWithField();
                    $attribute!=='username' && $attribute!=='email' && $attribute!=='usernameOrEmail' && ($attribute='username');
                    $this->addError($attribute,'');
                    break;
                case _CUserIdentity::ERROR_IS_NOT_ADMINISTRATOR:
                    // set the error message
                    MUserFlash::setTopError(Yii::t('feedback',
                        'We are sorry, but your access type is {accessType}. Required access type: {requiredAccessType}.',
                        array(
                            '{accessType}'=>Yii::app()->controller->var->userAccessType,
                            '{requiredAccessType}'=>Yii::t('t',User::ADMINISTRATOR_T)
                        )
                    ));
                    unset(Yii::app()->controller->var->userAccessType); // we do not need this any more
                    // add to username (first field in the login form) error css class
                    // and make the validate() to fail
                    $attribute=self::getLoggingWithField();
                    $attribute!=='username' && $attribute!=='email' && $attribute!=='usernameOrEmail' && ($attribute='username');
                    $this->addError($attribute,'');
                    break;
                case _CUserIdentity::ERROR_PASSWORD_INVALID:
                default:
                    $this->addError('password',Yii::t('t','Password is incorrect.'));
                    break;
            }
        }
    }

    /**
    * Which field to log user in with
    * 
    * @return string
    */
    public function getLoginWithField()
    {
        $array=array(
            'username'=>'username',
            'email'=>'email',
            '_any_'=>'usernameOrEmail'
        );
        return $array[MParams::getUserLoginWithField()];
    }

    /**
    * Which field is user now trying to login with
    * If trying to login with username, but later userLoginWithField param
    * was changed to email, still username should be required, not email, right?
    * 
    * @return string
    */
    public static function getLoggingWithField()
    {
        switch(self::$loginWithField)
        {
            case 'username':
            case 'email':
            case 'usernameOrEmail':
                return self::$loginWithField;
                break;
            default:
                return 'username';
                break;
        }
    }
}


Listing of _CUserIdentity component:
<?php
/**
 * _CUserIdentity represents the data needed to identity a user.
 * It contains the authentication method that checks if the provided
 * data can identity the user.
 */
class _CUserIdentity extends CUserIdentity
{
    const ERROR_UNKNOWN_IDENTITY=10;
    const ERROR_ACCOUNT_IS_INACTIVE=11;
    const ERROR_IS_NOT_ADMINISTRATOR=12;

    private $_id;

    /**
     * @return integer the ID of the user record
     */
    public function getId()
    {
        return $this->_id;
    }

    /**
     * Authenticates a user.
     * @return boolean whether authentication succeeds.
     */
    public function authenticate()
    {
        if(UserLoginForm::getLoggingWithField()=='username')
            $user=User::model()->findByAttributes(array('username'=>$this->username));
        else if(UserLoginForm::getLoggingWithField()=='email')
            $user=User::model()->findByAttributes(array('email'=>$this->username));
        else if(UserLoginForm::getLoggingWithField()=='usernameOrEmail')
            $user=User::model()->find("`username`=? OR `email`=?",array($this->username,$this->username));
        if($user===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if(md5($this->password)!==$user->password)
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else if($user->isActive===User::IS_NOT_ACTIVE)
            $this->errorCode=self::ERROR_ACCOUNT_IS_INACTIVE;
        else if(MArea::isBackend() && !User::isAdministrator($user->accessType))
        {
            $this->errorCode=self::ERROR_IS_NOT_ADMINISTRATOR;
            Yii::app()->controller->var->userAccessType=$user->getAttributeView('accessType');
        }
        else
        {
            $this->_id=$user->id;
            $this->errorCode=self::ERROR_NONE;
            // do not store password or other sensitive data in the persistent storage
            // when (config/main.php) allowAutoLogin is true, because
            // all these data will be stored in cookie = it is readable
            $this->setState('email', $user->email);
            $this->setState('interface', $user->interface);
            $this->setState('language', $user->language);
            $this->setState('screenName', $user->screenName);
        }
        return !$this->errorCode;
    }

    /**
     * Authenticates a user by cookie.
     * Is called by {@link _CWebUser::restoreFromCookie()}.
     * @return boolean whether authentication succeeds.
     */
    public function authenticateByCookie()
    {
        $user=User::model()->findByPk($this->username);
        if($user===null)
            $this->errorCode=self::ERROR_UNKNOWN_IDENTITY;
        else if($user->isActive==='0')
            $this->errorCode=self::ERROR_ACCOUNT_IS_INACTIVE;
        else
        {
            $this->_id=$user->id;
            $this->errorCode=self::ERROR_NONE;
            // do not store password or other sensitive data in the persistent storage
            // when (config/main.php) allowAutoLogin is true, because
            // all these data will be stored in cookie = it is readable
            $this->setState('email', $user->email);
            $this->setState('interface', $user->interface);
            $this->setState('language', $user->language);
            $this->setState('screenName', $user->screenName);
        }
        return !$this->errorCode;
    }
}

0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users