简体   繁体   中英

How can I set "remember me" cookie in Joomla custom component?

I would like to set "joomla_remember_me..." cookie from my custom script depending on condition. I would probably need to execute public function onUserAfterLogin from the file \\plugins\\authentication\\cookie\\cookie.php with the argument "remember me" set, but I cannot figure out how.

Here's the cookie.php file for the reference:

/**
 * Joomla Authentication plugin
 *
 * @since  3.2
 * @note   Code based on http://jaspan.com/improved_persistent_login_cookie_best_practice
 *         and http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/
 */
class PlgAuthenticationCookie extends JPlugin
{
    /**
     * Application object
     *
     * @var    JApplicationCms
     * @since  3.2
     */
    protected $app;

    /**
     * Database object
     *
     * @var    JDatabaseDriver
     * @since  3.2
     */
    protected $db;

    /**
     * Reports the privacy related capabilities for this plugin to site administrators.
     *
     * @return  array
     *
     * @since   3.9.0
     */
    public function onPrivacyCollectAdminCapabilities()
    {
        $this->loadLanguage();

        return array(
            JText::_('PLG_AUTHENTICATION_COOKIE') => array(
                JText::_('PLG_AUTH_COOKIE_PRIVACY_CAPABILITY_COOKIE'),
            )
        );
    }

    /**
     * This method should handle any authentication and report back to the subject
     *
     * @param   array   $credentials  Array holding the user credentials
     * @param   array   $options      Array of extra options
     * @param   object  &$response    Authentication response object
     *
     * @return  boolean
     *
     * @since   3.2
     */
    public function onUserAuthenticate($credentials, $options, &$response)
    {
        // No remember me for admin
        if ($this->app->isClient('administrator'))
        {
            return false;
        }

        // Get cookie
        $cookieName  = 'joomla_remember_me_' . JUserHelper::getShortHashedUserAgent();
        $cookieValue = $this->app->input->cookie->get($cookieName);

        // Try with old cookieName (pre 3.6.0) if not found
        if (!$cookieValue)
        {
            $cookieName  = JUserHelper::getShortHashedUserAgent();
            $cookieValue = $this->app->input->cookie->get($cookieName);
        }

        if (!$cookieValue)
        {
            return false;
        }

        $cookieArray = explode('.', $cookieValue);

        // Check for valid cookie value
        if (count($cookieArray) !== 2)
        {
            // Destroy the cookie in the browser.
            $this->app->input->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', ''));
            JLog::add('Invalid cookie detected.', JLog::WARNING, 'error');

            return false;
        }

        $response->type = 'Cookie';

        // Filter series since we're going to use it in the query
        $filter = new JFilterInput;
        $series = $filter->clean($cookieArray[1], 'ALNUM');

        // Remove expired tokens
        $query = $this->db->getQuery(true)
            ->delete('#__user_keys')
            ->where($this->db->quoteName('time') . ' < ' . $this->db->quote(time()));

        try
        {
            $this->db->setQuery($query)->execute();
        }
        catch (RuntimeException $e)
        {
            // We aren't concerned with errors from this query, carry on
        }

        // Find the matching record if it exists.
        $query = $this->db->getQuery(true)
            ->select($this->db->quoteName(array('user_id', 'token', 'series', 'time')))
            ->from($this->db->quoteName('#__user_keys'))
            ->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series))
            ->where($this->db->quoteName('uastring') . ' = ' . $this->db->quote($cookieName))
            ->order($this->db->quoteName('time') . ' DESC');

        try
        {
            $results = $this->db->setQuery($query)->loadObjectList();
        }
        catch (RuntimeException $e)
        {
            $response->status = JAuthentication::STATUS_FAILURE;

            return false;
        }

        if (count($results) !== 1)
        {
            // Destroy the cookie in the browser.
            $this->app->input->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', ''));
            $response->status = JAuthentication::STATUS_FAILURE;

            return false;
        }

        // We have a user with one cookie with a valid series and a corresponding record in the database.
        if (!JUserHelper::verifyPassword($cookieArray[0], $results[0]->token))
        {
            /*
             * This is a real attack!
             * Either the series was guessed correctly or a cookie was stolen and used twice (once by attacker and once by victim).
             * Delete all tokens for this user!
             */
            $query = $this->db->getQuery(true)
                ->delete('#__user_keys')
                ->where($this->db->quoteName('user_id') . ' = ' . $this->db->quote($results[0]->user_id));

            try
            {
                $this->db->setQuery($query)->execute();
            }
            catch (RuntimeException $e)
            {
                // Log an alert for the site admin
                JLog::add(
                    sprintf('Failed to delete cookie token for user %s with the following error: %s', $results[0]->user_id, $e->getMessage()),
                    JLog::WARNING,
                    'security'
                );
            }

            // Destroy the cookie in the browser.
            $this->app->input->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', ''));

            // Issue warning by email to user and/or admin?
            JLog::add(JText::sprintf('PLG_AUTH_COOKIE_ERROR_LOG_LOGIN_FAILED', $results[0]->user_id), JLog::WARNING, 'security');
            $response->status = JAuthentication::STATUS_FAILURE;

            return false;
        }

        // Make sure there really is a user with this name and get the data for the session.
        $query = $this->db->getQuery(true)
            ->select($this->db->quoteName(array('id', 'username', 'password')))
            ->from($this->db->quoteName('#__users'))
            ->where($this->db->quoteName('username') . ' = ' . $this->db->quote($results[0]->user_id))
            ->where($this->db->quoteName('requireReset') . ' = 0');

        try
        {
            $result = $this->db->setQuery($query)->loadObject();
        }
        catch (RuntimeException $e)
        {
            $response->status = JAuthentication::STATUS_FAILURE;

            return false;
        }

        if ($result)
        {
            // Bring this in line with the rest of the system
            $user = JUser::getInstance($result->id);

            // Set response data.
            $response->username = $result->username;
            $response->email    = $user->email;
            $response->fullname = $user->name;
            $response->password = $result->password;
            $response->language = $user->getParam('language');

            // Set response status.
            $response->status        = JAuthentication::STATUS_SUCCESS;
            $response->error_message = '';
        }
        else
        {
            $response->status        = JAuthentication::STATUS_FAILURE;
            $response->error_message = JText::_('JGLOBAL_AUTH_NO_USER');
        }
    }

    /**
     * We set the authentication cookie only after login is successfullly finished.
     * We set a new cookie either for a user with no cookies or one
     * where the user used a cookie to authenticate.
     *
     * @param   array  $options  Array holding options
     *
     * @return  boolean  True on success
     *
     * @since   3.2
     */
    public function onUserAfterLogin($options)
    {
        // No remember me for admin
        if ($this->app->isClient('administrator'))
        {
            return false;
        }

        if (isset($options['responseType']) && $options['responseType'] === 'Cookie')
        {
            // Logged in using a cookie
            $cookieName = 'joomla_remember_me_' . JUserHelper::getShortHashedUserAgent();

            // We need the old data to get the existing series
            $cookieValue = $this->app->input->cookie->get($cookieName);

            // Try with old cookieName (pre 3.6.0) if not found
            if (!$cookieValue)
            {
                $oldCookieName = JUserHelper::getShortHashedUserAgent();
                $cookieValue   = $this->app->input->cookie->get($oldCookieName);

                // Destroy the old cookie in the browser
                $this->app->input->cookie->set($oldCookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', ''));
            }

            $cookieArray = explode('.', $cookieValue);

            // Filter series since we're going to use it in the query
            $filter = new JFilterInput;
            $series = $filter->clean($cookieArray[1], 'ALNUM');
        }
        elseif (!empty($options['remember']))
        {
            // Remember checkbox is set
            $cookieName = 'joomla_remember_me_' . JUserHelper::getShortHashedUserAgent();

            // Create a unique series which will be used over the lifespan of the cookie
            $unique     = false;
            $errorCount = 0;

            do
            {
                $series = JUserHelper::genRandomPassword(20);
                $query  = $this->db->getQuery(true)
                    ->select($this->db->quoteName('series'))
                    ->from($this->db->quoteName('#__user_keys'))
                    ->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series));

                try
                {
                    $results = $this->db->setQuery($query)->loadResult();

                    if ($results === null)
                    {
                        $unique = true;
                    }
                }
                catch (RuntimeException $e)
                {
                    $errorCount++;

                    // We'll let this query fail up to 5 times before giving up, there's probably a bigger issue at this point
                    if ($errorCount === 5)
                    {
                        return false;
                    }
                }
            }

            while ($unique === false);
        }
        else
        {
            return false;
        }

        // Get the parameter values
        $lifetime = $this->params->get('cookie_lifetime', 60) * 24 * 60 * 60;
        $length   = $this->params->get('key_length', 16);

        // Generate new cookie
        $token       = JUserHelper::genRandomPassword($length);
        $cookieValue = $token . '.' . $series;

        // Overwrite existing cookie with new value
        $this->app->input->cookie->set(
            $cookieName,
            $cookieValue,
            time() + $lifetime,
            $this->app->get('cookie_path', '/'),
            $this->app->get('cookie_domain', ''),
            $this->app->isHttpsForced(),
            true
        );

        $query = $this->db->getQuery(true);

        if (!empty($options['remember']))
        {
            // Create new record
            $query
                ->insert($this->db->quoteName('#__user_keys'))
                ->set($this->db->quoteName('user_id') . ' = ' . $this->db->quote($options['user']->username))
                ->set($this->db->quoteName('series') . ' = ' . $this->db->quote($series))
                ->set($this->db->quoteName('uastring') . ' = ' . $this->db->quote($cookieName))
                ->set($this->db->quoteName('time') . ' = ' . (time() + $lifetime));
        }
        else
        {
            // Update existing record with new token
            $query
                ->update($this->db->quoteName('#__user_keys'))
                ->where($this->db->quoteName('user_id') . ' = ' . $this->db->quote($options['user']->username))
                ->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series))
                ->where($this->db->quoteName('uastring') . ' = ' . $this->db->quote($cookieName));
        }

        $hashedToken = JUserHelper::hashPassword($token);

        $query->set($this->db->quoteName('token') . ' = ' . $this->db->quote($hashedToken));

        try
        {
            $this->db->setQuery($query)->execute();
        }
        catch (RuntimeException $e)
        {
            return false;
        }

        return true;
    }

    /**
     * This is where we delete any authentication cookie when a user logs out
     *
     * @param   array  $options  Array holding options (length, timeToExpiration)
     *
     * @return  boolean  True on success
     *
     * @since   3.2
     */
    public function onUserAfterLogout($options)
    {
        // No remember me for admin
        if ($this->app->isClient('administrator'))
        {
            return false;
        }

        $cookieName  = 'joomla_remember_me_' . JUserHelper::getShortHashedUserAgent();
        $cookieValue = $this->app->input->cookie->get($cookieName);

        // There are no cookies to delete.
        if (!$cookieValue)
        {
            return true;
        }

        $cookieArray = explode('.', $cookieValue);

        // Filter series since we're going to use it in the query
        $filter = new JFilterInput;
        $series = $filter->clean($cookieArray[1], 'ALNUM');

        // Remove the record from the database
        $query = $this->db->getQuery(true)
            ->delete('#__user_keys')
            ->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series));

        try
        {
            $this->db->setQuery($query)->execute();
        }
        catch (RuntimeException $e)
        {
            // We aren't concerned with errors from this query, carry on
        }

        // Destroy the cookie
        $this->app->input->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', ''));

        return true;
    }
}

Thanks in advance :)

It's a very complex process and I think you would be better off cloning this plugin to a new name, adding your condition to the function, unpublish the current plugin and publish your new one instead. You will also have to watch for any changes to the original plugin in case a security issue is found.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM