Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
50.00% covered (danger)
50.00%
1 / 2
CRAP
44.79% covered (danger)
44.79%
43 / 96
db
0.00% covered (danger)
0.00%
0 / 1
50.00% covered (danger)
50.00%
1 / 2
159.93
44.79% covered (danger)
44.79%
43 / 96
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
9 / 9
 login
0.00% covered (danger)
0.00%
0 / 1
191.82
39.08% covered (danger)
39.08%
34 / 87
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\auth\provider;
use phpbb\captcha\factory;
use phpbb\captcha\plugins\captcha_abstract;
use phpbb\config\config;
use phpbb\db\driver\driver_interface;
use phpbb\passwords\manager;
use phpbb\request\request_interface;
use phpbb\user;
/**
 * Database authentication provider for phpBB3
 * This is for authentication via the integrated user table
 */
class db extends base
{
    /** @var factory CAPTCHA factory */
    protected $captcha_factory;
    /** @var config phpBB config */
    protected $config;
    /** @var driver_interface DBAL driver instance */
    protected $db;
    /** @var request_interface Request object */
    protected $request;
    /** @var user User object */
    protected $user;
    /** @var string phpBB root path */
    protected $phpbb_root_path;
    /** @var string PHP file extension */
    protected $php_ext;
    /**
    * phpBB passwords manager
    *
    * @var manager
    */
    protected $passwords_manager;
    /**
     * Database Authentication Constructor
     *
     * @param factory $captcha_factory
     * @param    config         $config
     * @param    driver_interface        $db
     * @param    manager    $passwords_manager
     * @param    request_interface        $request
     * @param    user            $user
     * @param    string                $phpbb_root_path
     * @param    string                $php_ext
     */
    public function __construct(factory $captcha_factory, config $config, driver_interface $db, manager $passwords_manager, request_interface $request, user $user, $phpbb_root_path, $php_ext)
    {
        $this->captcha_factory = $captcha_factory;
        $this->config = $config;
        $this->db = $db;
        $this->passwords_manager = $passwords_manager;
        $this->request = $request;
        $this->user = $user;
        $this->phpbb_root_path = $phpbb_root_path;
        $this->php_ext = $php_ext;
    }
    /**
     * {@inheritdoc}
     */
    public function login($username, $password)
    {
        // Auth plugins get the password untrimmed.
        // For compatibility we trim() here.
        $password = trim($password);
        // do not allow empty password
        if (!$password)
        {
            return array(
                'status'    => LOGIN_ERROR_PASSWORD,
                'error_msg'    => 'NO_PASSWORD_SUPPLIED',
                'user_row'    => array('user_id' => ANONYMOUS),
            );
        }
        if (!$username)
        {
            return array(
                'status'    => LOGIN_ERROR_USERNAME,
                'error_msg'    => 'LOGIN_ERROR_USERNAME',
                'user_row'    => array('user_id' => ANONYMOUS),
            );
        }
        $username_clean = utf8_clean_string($username);
        $sql = 'SELECT *
            FROM ' . USERS_TABLE . "
            WHERE username_clean = '" . $this->db->sql_escape($username_clean) . "'";
        $result = $this->db->sql_query($sql);
        $row = $this->db->sql_fetchrow($result);
        $this->db->sql_freeresult($result);
        if (($this->user->ip && !$this->config['ip_login_limit_use_forwarded']) ||
            ($this->user->forwarded_for && $this->config['ip_login_limit_use_forwarded']))
        {
            $sql = 'SELECT COUNT(*) AS attempts
                FROM ' . LOGIN_ATTEMPT_TABLE . '
                WHERE attempt_time > ' . (time() - (int) $this->config['ip_login_limit_time']);
            if ($this->config['ip_login_limit_use_forwarded'])
            {
                $sql .= " AND attempt_forwarded_for = '" . $this->db->sql_escape($this->user->forwarded_for) . "'";
            }
            else
            {
                $sql .= " AND attempt_ip = '" . $this->db->sql_escape($this->user->ip) . "' ";
            }
            $result = $this->db->sql_query($sql);
            $attempts = (int) $this->db->sql_fetchfield('attempts');
            $this->db->sql_freeresult($result);
            $attempt_data = array(
                'attempt_ip'            => $this->user->ip,
                'attempt_browser'        => trim(substr($this->user->browser, 0, 149)),
                'attempt_forwarded_for'    => $this->user->forwarded_for,
                'attempt_time'            => time(),
                'user_id'                => ($row) ? (int) $row['user_id'] : 0,
                'username'                => $username,
                'username_clean'        => $username_clean,
            );
            $sql = 'INSERT INTO ' . LOGIN_ATTEMPT_TABLE . $this->db->sql_build_array('INSERT', $attempt_data);
            $this->db->sql_query($sql);
        }
        else
        {
            $attempts = 0;
        }
        $login_error_attempts = 'LOGIN_ERROR_ATTEMPTS';
        $user_login_attempts    = (is_array($row) && $this->config['max_login_attempts'] && $row['user_login_attempts'] >= $this->config['max_login_attempts']);
        $ip_login_attempts        = ($this->config['ip_login_limit_max'] && $attempts >= $this->config['ip_login_limit_max']);
        $show_captcha = $user_login_attempts || $ip_login_attempts;
        if ($show_captcha)
        {
            $captcha = $this->captcha_factory->get_instance($this->config['captcha_plugin']);
            // Get custom message for login error when exceeding maximum number of attempts
            if ($captcha instanceof captcha_abstract)
            {
                $login_error_attempts = $captcha->get_login_error_attempts();
            }
        }
        if (!$row)
        {
            if ($this->config['ip_login_limit_max'] && $attempts >= $this->config['ip_login_limit_max'])
            {
                return array(
                    'status'        => LOGIN_ERROR_ATTEMPTS,
                    'error_msg'        => $login_error_attempts,
                    'user_row'        => array('user_id' => ANONYMOUS),
                );
            }
            return array(
                'status'    => LOGIN_ERROR_USERNAME,
                'error_msg'    => 'LOGIN_ERROR_USERNAME',
                'user_row'    => array('user_id' => ANONYMOUS),
            );
        }
        // If there are too many login attempts, we need to check for a confirm image
        // Every auth module is able to define what to do by itself...
        if ($show_captcha)
        {
            $captcha->init(CONFIRM_LOGIN);
            $vc_response = $captcha->validate($row);
            if ($vc_response)
            {
                return array(
                    'status'        => LOGIN_ERROR_ATTEMPTS,
                    'error_msg'        => $login_error_attempts,
                    'user_row'        => $row,
                );
            }
            else
            {
                $captcha->reset();
            }
        }
        // Check password ...
        if ($this->passwords_manager->check($password, $row['user_password'], $row))
        {
            // Check for old password hash...
            if ($this->passwords_manager->convert_flag || strlen($row['user_password']) == 32)
            {
                $hash = $this->passwords_manager->hash($password);
                // Update the password in the users table to the new format
                $sql = 'UPDATE ' . USERS_TABLE . "
                    SET user_password = '" . $this->db->sql_escape($hash) . "'
                    WHERE user_id = {$row['user_id']}";
                $this->db->sql_query($sql);
                $row['user_password'] = $hash;
            }
            $sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . '
                WHERE user_id = ' . $row['user_id'];
            $this->db->sql_query($sql);
            if ($row['user_login_attempts'] != 0)
            {
                // Successful, reset login attempts (the user passed all stages)
                $sql = 'UPDATE ' . USERS_TABLE . '
                    SET user_login_attempts = 0
                    WHERE user_id = ' . $row['user_id'];
                $this->db->sql_query($sql);
            }
            // User inactive...
            if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE)
            {
                return array(
                    'status'        => LOGIN_ERROR_ACTIVE,
                    'error_msg'        => 'ACTIVE_ERROR',
                    'user_row'        => $row,
                );
            }
            // Successful login... set user_login_attempts to zero...
            return array(
                'status'        => LOGIN_SUCCESS,
                'error_msg'        => false,
                'user_row'        => $row,
            );
        }
        // Password incorrect - increase login attempts
        $sql = 'UPDATE ' . USERS_TABLE . '
            SET user_login_attempts = user_login_attempts + 1
            WHERE user_id = ' . (int) $row['user_id'] . '
                AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX;
        $this->db->sql_query($sql);
        // Give status about wrong password...
        return array(
            'status'        => ($show_captcha) ? LOGIN_ERROR_ATTEMPTS : LOGIN_ERROR_PASSWORD,
            'error_msg'        => 'LOGIN_ERROR_PASSWORD',
            'user_row'        => $row,
        );
    }
}