<?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;

use Joomla\Authentication\Password\BCryptHandler;
use Joomla\Authentication\Password\HandlerInterface;

/**
 * Abstract AuthenticationStrategy for username/password based authentication
 *
 * @since  1.1.0
 */
abstract class AbstractUsernamePasswordAuthenticationStrategy implements AuthenticationStrategyInterface
{
	/**
	 * The password handler to validate the password against.
	 *
	 * @var    HandlerInterface
	 * @since  1.2.0
	 */
	protected $passwordHandler;

	/**
	 * The last authentication status.
	 *
	 * @var    integer
	 * @since  1.1.0
	 */
	protected $status;

	/**
	 * Constructor.
	 *
	 * @param   HandlerInterface  $passwordHandler  The password handler.
	 *
	 * @since   1.2.0
	 */
	public function __construct(?HandlerInterface $passwordHandler = null)
	{
		$this->passwordHandler = $passwordHandler ?: new BCryptHandler;
	}

	/**
	 * Attempt to authenticate the username and password pair.
	 *
	 * @param   string  $username  The username to authenticate.
	 * @param   string  $password  The password to attempt authentication with.
	 *
	 * @return  string|boolean  A string containing a username if authentication is successful, false otherwise.
	 *
	 * @since   1.1.0
	 */
	protected function doAuthenticate($username, $password)
	{
		$hashedPassword = $this->getHashedPassword($username);

		if ($hashedPassword === false)
		{
			$this->status = Authentication::NO_SUCH_USER;

			return false;
		}

		if (!$this->verifyPassword($username, $password, $hashedPassword))
		{
			$this->status = Authentication::INVALID_CREDENTIALS;

			return false;
		}

		$this->status = Authentication::SUCCESS;

		return $username;
	}

	/**
	 * 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
	 */
	abstract protected function getHashedPassword($username);

	/**
	 * Get the status of the last authentication attempt.
	 *
	 * @return  integer  Authentication class constant result.
	 *
	 * @since   1.1.0
	 */
	public function getResult()
	{
		return $this->status;
	}

	/**
	 * Attempt to verify the username and password pair.
	 *
	 * @param   string  $username        The username to authenticate.
	 * @param   string  $password        The password to attempt authentication with.
	 * @param   string  $hashedPassword  The hashed password to attempt authentication against.
	 *
	 * @return  boolean
	 *
	 * @since   1.1.0
	 */
	protected function verifyPassword($username, $password, $hashedPassword)
	{
		return $this->passwordHandler->validatePassword($password, $hashedPassword);
	}
}
