• Index
  • » Users
  • » alc0905
  • » Posts

Posts

Posts

Hi there!

Just wanted to provide you with a follow-up. I managed to solve the issue but it's messy - some hacking of the core files, which I hate to do, but had no other options at this point. Here is what I did:

EDIT: To show the location of the changes I made, I highlighted any code modifications in bold, but the bold formatting doesn't show - you will see the

Code:

[b] [/b]

around the modified code though!

1) I modified the function onLogoutUser in the  joomla core user plugin, located at plugins/user/joomla.php

Code:

function onLogoutUser($user, $options = array())

    {
        $my =& JFactory::getUser();
        //Make sure we're a valid user first
        if($user['id'] == 0 && !$my->get('tmp_user')) return true;

        //Check to see if we're deleting the current session
        if($my->get('id') == $user['id'])
        {
            [b]// check to see if last vist is 0
            if($my->lastvisitDate != "0000-00-00 00:00:00") :
                // Hit the user last visit field
                $my->setLastVisit();
            endif;[/b]
           
            // Destroy the php session for this user
            $session =& JFactory::getSession();
            $session->destroy();
        } else {
            // Force logout all users with that userid
            $table = & JTable::getInstance('session');
            $table->destroy($user['id'], $options['clientid']);
        }
        return true;
    }

I noticed that every time a user is logged out, by default, their last visit date is automatically updated, which proved troublesome if a user logs in for the first time, fails to change their password successfully, and then logs out. This conditional check makes it so that the last visit date is updated only if the last visit date is not set to zero.

2) Updated the save function in the com_user controller file, located at components/com_user_controller.php

Code:

function save()

    {
        // Check for request forgeries
        JRequest::checkToken() or jexit( 'Invalid Token' );

        $user     =& JFactory::getUser();
        $userid = JRequest::getVar( 'id', 0, 'post', 'int' );

        // preform security checks
        if ($user->get('id') == 0 || $userid == 0 || $userid <> $user->get('id')) {
            JError::raiseError( 403, JText::_('Access Forbidden') );
            return;
        }

        //clean request
        $post = JRequest::get( 'post' );
        $post['username']    = JRequest::getVar('username', '', 'post', 'username');
        $post['password']    = JRequest::getVar('password', '', 'post', 'string', JREQUEST_ALLOWRAW);
        $post['password2']    = JRequest::getVar('password2', '', 'post', 'string', JREQUEST_ALLOWRAW);
   
        // get the redirect
        $return = JURI::base();
       
        // do a password safety check
        if((preg_match("/^.*(?=.{6,})(?=.*[a-z])(?=.*[\d]).*$/", $post['password'])) || (preg_match("/^.*(?=.{6,})(?=.*[a-z])(?=.*[\d]).*$/", $post['password2']))) { // password must be strong
            if($post['password'] != $post['password2']) {
                $msg    = JText::_('PASSWORDS_DO_NOT_MATCH');
                // something is wrong. we are redirecting back to edit form.
                // TODO: HTTP_REFERER should be replaced with a base64 encoded form field in a later release
                $return = str_replace(array('"', '<', '>', "'"), '', @$_SERVER['HTTP_REFERER']);
                if (empty($return) || !JURI::isInternal($return)) {
                    $return = JURI::base();
                }
                $this->setRedirect($return, $msg, 'error');
                return false;
            }
        } else {
            $msg    = JText::_('Password does not meet security requirements. Please try again.');
            $return = str_replace(array('"', '<', '>', "'"), '', @$_SERVER['HTTP_REFERER']);
            if (empty($return) || !JURI::isInternal($return)) {
                $return = JURI::base();
            }
            $this->setRedirect($return, $msg, 'error');
            return false;
        }

        // we don't want users to edit certain fields so we will unset them
        unset($post['gid']);
        unset($post['block']);
        unset($post['usertype']);
        unset($post['registerDate']);
        unset($post['activation']);

        // store data
        $model = $this->getModel('user');
       
        $return = str_replace(array('"', '<', '>', "'"), '', @$_SERVER['HTTP_REFERER']);
        if (empty($return) || !JURI::isInternal($return)) {
            $return = JURI::base();
        }

        if ($model->store($post)) {
            [b]$user->setLastVisit();
            $date = JFactory::getDate();
            $user->lastvisitDate = $date->toMySQL();[/b]
            $msg    = JText::_( 'Your account was successfully updated.' );
            $this->setRedirect( $return, $msg );
        } else {
            //$msg    = JText::_( 'Error saving your settings.' );
            $msg    = $model->getError();
            $this->setRedirect( $return, $msg, 'error' );
        }
    }

Besides the changes I made to enforce strong password validation, the code basically updates the user's last visit date when the user successfully updates his or her account. When using the force password change plugin, users are automatically brought to the edit user form, where they must change their password. So, when a user changes his or her password, this function is called, if the password validation passes -> the last visit date is updated and the changes are saved. (Now every time a user logs out, their last visit date is automatically updated as normal).

3) Changes to forcepasswordchange.php.

Code:

class plgSystemForcePasswordChange extends JPlugin

{
    function plgSystemForcePasswordChange(&$subject, $config)
    {
        parent::__construct($subject, $config);
    }

    function onAfterRoute()
    {
                global $mainframe;

                // Don't do anything if this is the administrator backend or debugging is on
                if($mainframe->isAdmin() || JDEBUG) {
                        return;
                }

        $user = &JFactory::getUser();

        $option = JRequest::getVar('option');
        $view = JRequest::getVar('view');
        $task = JRequest::getVar('task');
        $layout = JRequest::getVar('layout');
        // no_html is sent by Mighty Registration for ajax checks, so we need to ignore them
        $noHtml = JRequest::getVar('no_html');

        $editProfileOption = "com_user";
        $editProfileLayout = "form";
        $editProfileSaveTask = "save";
        $editProfileView = "user";
        [b]$logout = "logout";[/b]

        // Use these for Mighty Registration
        /*
        $editProfileOption = "com_juser";
        $editProfileLayout = "mydetails";
        $editProfileSaveTask = "user_update";
        */

        [b]if(!$user->guest && $user->lastvisitDate == "0000-00-00 00:00:00" && $noHtml != "1")
        {
            // The user is not a guest and their lastvisitDate is zeros
            if($option == $editProfileOption && $task == $editProfileSaveTask)
            {
                // The user is saving their profile

                // Set the last visit date to a real value so we won't continue forcing them to update their profile
                //$user->setLastVisit();
                //$date = JFactory::getDate();
                //$user->lastvisitDate = $date->toMySQL();
            }
            else if($task == $logout)
            {
                // do nothing
            }[/b]
            else if(!($option == $editProfileOption && $view == $editProfileView && $layout == $editProfileLayout))
            {
                // The user is not on the edit profile form

                // Update lastvisitDate back to zero
                $dbo = &JFactory::getDBO();
                $query = "UPDATE #__users ".
                    "SET lastvisitDate = ".$dbo->quote("0000-00-00 00:00:00")." ".
                    "WHERE id = ".$dbo->quote($user->id);
                $dbo->setQuery($query);
                $dbo->query();
   
                // Redirect to edit profile
                $app = &JFactory::getApplication();

                $app->redirect(
                    "index.php?option=".$editProfileOption."&view=".$editProfileView."&layout=".$editProfileLayout,
                    $this->params->get("message", "You must update your password before continuing to use the site.")
                );
            }
        }

I created a variable called logout and assigned  a string to it with the same name. I commented out the code in the first if statement, as I had moved that code into the save function in the controller.php file mentioned above. I added a second if statement to allow users to use the logout function, so they could logout of joomla, and log back in (and if they didnt not change their password, they will be brought back to the edit user form again automatically).

I think I covered most of what I changed. I am working off memory as I was recently away for vacation, but that is the gist of it current/smile

Messy hacks, but what can ya do!

Antonio

Hi! Thank you for this awesome plugin - installation was a breeze, and I already see it working in action.

I have a slight problem though. When users first login, they are brought to the standard edit user form as expected, where they can change their password. However, if the password update fails due to failing validation, the plugin still assumes the password change happened and updates the user's last visit date. The user can then browse the remainder of the website, despite not having really changed his or her password.

I have not modified any plugin code yet.

I have made a slight modification to the components\com_user\controller.php file to force a strong password using regular expressions. Here is the code I changed (approx line 81):

Code:

// do a password safety check

        if((preg_match("/^.*(?=.{6,})(?=.*[a-z])(?=.*[\d]).*$/", $post['password'])) || (preg_match("/^.*(?=.{6,})(?=.*[a-z])(?=.*[\d]).*$/", $post['password2']))) { // password must be strong
            if($post['password'] != $post['password2']) {
                $msg    = JText::_('PASSWORDS_DO_NOT_MATCH');
                // something is wrong. we are redirecting back to edit form.
                // TODO: HTTP_REFERER should be replaced with a base64 encoded form field in a later release
                $return = str_replace(array('"', '<', '>', "'"), '', @$_SERVER['HTTP_REFERER']);
                if (empty($return) || !JURI::isInternal($return)) {
                    $return = JURI::base();
                }
                $this->setRedirect($return, $msg, 'error');
                return false;
            }
        } else {
            $msg    = JText::_('Password does not meet security requirements.');
            $return = str_replace(array('"', '<', '>', "'"), '', @$_SERVER['HTTP_REFERER']);
            if (empty($return) || !JURI::isInternal($return)) {
                $return = JURI::base();
            }
            $this->setRedirect($return, $msg, 'error');
            return false;
        }

With this code, the user remains on the edit user form page if the password change operation fails due to failing validation and error messages are displayed.

Thanks for your help in this issue.

Antonio

  • Index
  • » Users
  • » alc0905
  • » Posts

Board Info

Board Stats
 
Total Topics:
11640
Total Polls:
0
Total Posts:
48895
Posts today:
1
User Info
 
Total Users:
47748
Newest User:
SamRodgers4
Members Online:
3
Guests Online:
759

Social Stream
SourceCoast Joomla Social Extensions
Friday, 25 May 2018
JFBConnect 8.0 Coming Soon! Stay tuned.
Refer and Earn

Like our extensions? You're not alone! That's why they're used on tens of thousands of websites. Now you can earn 20% of any sale you refer to SourceCoast.com.

We provide excellent documentation, well laid out sales pages and great support. All you need to do is spread the word and we'll take care of the rest.

Learn About Our Affiliate Program