<?php
/**
 * Part of the Joomla Framework Authentication Package
 *
 * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE
 */

namespace Joomla\Authentication\Strategies;

use Joomla\Authentication\AbstractUsernamePasswordAuthenticationStrategy;
use Joomla\Authentication\Authentication;
use Joomla\Authentication\Password\HandlerInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\Input\Input;

/**
 * Joomla Framework Database Strategy Authentication class
 *
 * @since  1.1.0
 */
class DatabaseStrategy extends AbstractUsernamePasswordAuthenticationStrategy
{
	/**
	 * DatabaseInterface object
	 *
	 * @var    DatabaseInterface
	 * @since  1.1.0
	 */
	private $db;

	/**
	 * Database connection options
	 *
	 * @var    array
	 * @since  1.1.0
	 */
	private $dbOptions;

	/**
	 * The Input object
	 *
	 * @var    Input
	 * @since  1.1.0
	 */
	private $input;

	/**
	 * Strategy Constructor
	 *
	 * @param   Input              $input            The input object from which to retrieve the request credentials.
	 * @param   DatabaseInterface  $database         DatabaseDriver for retrieving user credentials.
	 * @param   array              $options          Optional options array for configuring the credential storage connection.
	 * @param   HandlerInterface   $passwordHandler  The password handler.
	 *
	 * @since   1.1.0
	 */
	public function __construct(Input $input, DatabaseInterface $database, array $options = [], ?HandlerInterface $passwordHandler = null)
	{
		parent::__construct($passwordHandler);

		$this->input = $input;
		$this->db    = $database;

		$options['database_table']  = $options['database_table'] ?? '#__users';
		$options['username_column'] = $options['username_column'] ?? 'username';
		$options['password_column'] = $options['password_column'] ?? 'password';

		$this->dbOptions = $options;
	}

	/**
	 * Attempt to authenticate the username and password pair.
	 *
	 * @return  string|boolean  A string containing a username if authentication is successful, false otherwise.
	 *
	 * @since   1.1.0
	 */
	public function authenticate()
	{
		$username = $this->input->get('username', false, 'username');
		$password = $this->input->get('password', false, 'raw');

		if (!$username || !$password)
		{
			$this->status = Authentication::NO_CREDENTIALS;

			return false;
		}

		return $this->doAuthenticate($username, $password);
	}

	/**
	 * Retrieve the hashed password for the specified user.
	 *
	 * @param   string  $username  Username to lookup.
	 *
	 * @return  string|boolean  Hashed password on success or boolean false on failure.
	 *
	 * @since   1.1.0
	 */
	protected function getHashedPassword($username)
	{
		try
		{
			$password = $this->db->setQuery(
				$this->db->getQuery(true)
					->select($this->db->quoteName($this->dbOptions['password_column']))
					->from($this->db->quoteName($this->dbOptions['database_table']))
					->where($this->db->quoteName($this->dbOptions['username_column']) . ' = ?')
					->bind(1, $username)
			)->loadResult();
		}
		catch (\RuntimeException $exception)
		{
			return false;
		}

		if (!$password)
		{
			return false;
		}

		return $password;
	}
}
