primo commit

This commit is contained in:
2024-12-17 17:34:10 +01:00
commit e650f8df99
16435 changed files with 2451012 additions and 0 deletions

View File

@ -0,0 +1,119 @@
<?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);
}
}

View File

@ -0,0 +1,142 @@
<?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;
/**
* Joomla Framework Authentication Class
*
* @since 1.0
*/
class Authentication
{
/**
* Authentication was successful.
*
* @since 1.0
*/
public const SUCCESS = 1;
/**
* Credentials were provided but they were invalid.
*
* @since 1.0
*/
public const INVALID_CREDENTIALS = 2;
/**
* Credentials were provided but the user did not exist in the credential store.
*
* @since 1.0
*/
public const NO_SUCH_USER = 3;
/**
* There were no credentials found.
*
* @since 1.0
*/
public const NO_CREDENTIALS = 4;
/**
* There were partial credentials found but they were not complete.
*
* @since 1.0
*/
public const INCOMPLETE_CREDENTIALS = 5;
/**
* The array of strategies.
*
* @var AuthenticationStrategyInterface[]
* @since 1.0
*/
private $strategies = [];
/**
* The array of results.
*
* @var integer[]
* @since 1.0
*/
private $results = [];
/**
* Register a new strategy
*
* @param string $strategyName The name to use for the strategy.
* @param AuthenticationStrategyInterface $strategy The authentication strategy object to add.
*
* @return void
*
* @since 1.0
*/
public function addStrategy($strategyName, AuthenticationStrategyInterface $strategy)
{
$this->strategies[$strategyName] = $strategy;
}
/**
* Perform authentication
*
* @param string[] $strategies Array of strategies to try - empty to try all strategies.
*
* @return string|boolean A string containing a username if authentication is successful, false otherwise.
*
* @since 1.0
* @throws \RuntimeException
*/
public function authenticate(array $strategies = [])
{
if (empty($strategies)) {
$strategyObjects = $this->strategies;
} else {
$strategyObjects = [];
foreach ($strategies as $strategy) {
if (!isset($this->strategies[$strategy])) {
throw new \RuntimeException('Authentication Strategy Not Found');
}
$strategyObjects[$strategy] = $this->strategies[$strategy];
}
}
if (empty($strategyObjects)) {
throw new \RuntimeException('No strategies have been set');
}
/** @var AuthenticationStrategyInterface $strategyObject */
foreach ($strategyObjects as $strategy => $strategyObject) {
$username = $strategyObject->authenticate();
$this->results[$strategy] = $strategyObject->getResult();
if (\is_string($username)) {
return $username;
}
}
return false;
}
/**
* Get authentication results.
*
* Use this if you want to get more detailed information about the results of an authentication attempts.
*
* @return integer[] An array containing authentication results.
*
* @since 1.0
*/
public function getResults()
{
return $this->results;
}
}

View File

@ -0,0 +1,36 @@
<?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;
/**
* Joomla Framework AuthenticationStrategy Interface
*
* @since 1.0
*/
interface AuthenticationStrategyInterface
{
/**
* Attempt authentication.
*
* @return string|boolean A string containing a username if authentication is successful, false otherwise.
*
* @since 1.0
*/
public function authenticate();
/**
* Get last authentication result.
*
* @return integer An integer from Authentication class constants with the authentication result.
*
* @since 1.0
*/
public function getResult();
}

View File

@ -0,0 +1,19 @@
<?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\Exception;
/**
* Exception class defining an unsupported password handler
*
* @since 2.0.0
*/
class UnsupportedPasswordHandlerException extends \LogicException
{
}

View File

@ -0,0 +1,125 @@
<?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\Password;
use Joomla\Authentication\Exception\UnsupportedPasswordHandlerException;
/**
* Password handler for Argon2i hashed passwords
*
* @since 1.2.0
*/
class Argon2iHandler implements HandlerInterface
{
/**
* Generate a hash for a plaintext password
*
* @param string $plaintext The plaintext password to validate
* @param array $options Options for the hashing operation
*
* @return string
*
* @since 1.2.0
* @throws UnsupportedPasswordHandlerException if the password handler is not supported
*/
public function hashPassword($plaintext, array $options = [])
{
// Use the password extension if able
if (\defined('PASSWORD_ARGON2I')) {
return password_hash($plaintext, \PASSWORD_ARGON2I, $options);
}
// Use the sodium extension (PHP 7.2 native or PECL 2.x) if able
if (\function_exists('sodium_crypto_pwhash_str_verify')) {
$hash = sodium_crypto_pwhash_str(
$plaintext,
\SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
\SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
sodium_memzero($plaintext);
return $hash;
}
// Use the libsodium extension (PECL 1.x) if able
if (\extension_loaded('libsodium')) {
$hash = \Sodium\crypto_pwhash_str(
$plaintext,
\Sodium\CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
\Sodium\CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
\Sodium\memzero($plaintext);
return $hash;
}
throw new UnsupportedPasswordHandlerException('Argon2i algorithm is not supported.');
}
/**
* Check that the password handler is supported in this environment
*
* @return boolean
*
* @since 1.2.0
*/
public static function isSupported()
{
// Check for native PHP engine support in the password extension
if (\defined('PASSWORD_ARGON2I')) {
return true;
}
// Check if the sodium_compat polyfill is installed and look for compatibility through that
if (class_exists('\\ParagonIE_Sodium_Compat') && method_exists('\\ParagonIE_Sodium_Compat', 'crypto_pwhash_is_available')) {
return \ParagonIE_Sodium_Compat::crypto_pwhash_is_available();
}
// Check for support from the (lib)sodium extension
return \function_exists('sodium_crypto_pwhash_str') || \extension_loaded('libsodium');
}
/**
* Validate a password
*
* @param string $plaintext The plain text password to validate
* @param string $hashed The password hash to validate against
*
* @return boolean
*
* @since 1.2.0
* @throws UnsupportedPasswordHandlerException if the password handler is not supported
*/
public function validatePassword($plaintext, $hashed)
{
// Use the password extension if able
if (\defined('PASSWORD_ARGON2I')) {
return password_verify($plaintext, $hashed);
}
// Use the sodium extension (PHP 7.2 native or PECL 2.x) if able
if (\function_exists('sodium_crypto_pwhash_str_verify')) {
$valid = sodium_crypto_pwhash_str_verify($hashed, $plaintext);
sodium_memzero($plaintext);
return $valid;
}
// Use the libsodium extension (PECL 1.x) if able
if (\extension_loaded('libsodium')) {
$valid = \Sodium\crypto_pwhash_str_verify($hashed, $plaintext);
\Sodium\memzero($plaintext);
return $valid;
}
throw new UnsupportedPasswordHandlerException('Argon2i algorithm is not supported.');
}
}

View File

@ -0,0 +1,79 @@
<?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\Password;
use Joomla\Authentication\Exception\UnsupportedPasswordHandlerException;
/**
* Password handler for Argon2id hashed passwords
*
* @since 1.3.0
*/
class Argon2idHandler implements HandlerInterface
{
/**
* Generate a hash for a plaintext password
*
* @param string $plaintext The plaintext password to validate
* @param array $options Options for the hashing operation
*
* @return string
*
* @since 1.3.0
* @throws UnsupportedPasswordHandlerException if the password handler is not supported
*/
public function hashPassword($plaintext, array $options = [])
{
// Use the password extension if able
if (version_compare(\PHP_VERSION, '7.3', '>=') && \defined('PASSWORD_ARGON2ID')) {
return password_hash($plaintext, \PASSWORD_ARGON2ID, $options);
}
throw new UnsupportedPasswordHandlerException('Argon2id algorithm is not supported.');
}
/**
* Check that the password handler is supported in this environment
*
* @return boolean
*
* @since 1.3.0
*/
public static function isSupported()
{
// Check for native PHP engine support in the password extension
if (version_compare(\PHP_VERSION, '7.3', '>=') && \defined('PASSWORD_ARGON2ID')) {
return true;
}
return false;
}
/**
* Validate a password
*
* @param string $plaintext The plain text password to validate
* @param string $hashed The password hash to validate against
*
* @return boolean
*
* @since 1.3.0
* @throws UnsupportedPasswordHandlerException if the password handler is not supported
*/
public function validatePassword($plaintext, $hashed)
{
// Use the password extension if able
if (version_compare(\PHP_VERSION, '7.3', '>=') && \defined('PASSWORD_ARGON2ID')) {
return password_verify($plaintext, $hashed);
}
throw new UnsupportedPasswordHandlerException('Argon2id algorithm is not supported.');
}
}

View File

@ -0,0 +1,61 @@
<?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\Password;
/**
* Password handler for BCrypt hashed passwords
*
* @since 1.2.0
*/
class BCryptHandler implements HandlerInterface
{
/**
* Generate a hash for a plaintext password
*
* @param string $plaintext The plaintext password to validate
* @param array $options Options for the hashing operation
*
* @return string
*
* @since 1.2.0
*/
public function hashPassword($plaintext, array $options = [])
{
return password_hash($plaintext, \PASSWORD_BCRYPT, $options);
}
/**
* Check that the password handler is supported in this environment
*
* @return boolean
*
* @since 1.2.0
*/
public static function isSupported()
{
// Check the password_verify() function exists, either as part of PHP core or through a polyfill
return \function_exists('password_verify');
}
/**
* Validate a password
*
* @param string $plaintext The plain text password to validate
* @param string $hashed The password hash to validate against
*
* @return boolean
*
* @since 1.2.0
*/
public function validatePassword($plaintext, $hashed)
{
return password_verify($plaintext, $hashed);
}
}

View File

@ -0,0 +1,51 @@
<?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\Password;
/**
* Interface defining a password handler
*
* @since 1.2.0
*/
interface HandlerInterface
{
/**
* Generate a hash for a plaintext password
*
* @param string $plaintext The plaintext password to validate
* @param array $options Options for the hashing operation
*
* @return string
*
* @since 1.2.0
*/
public function hashPassword($plaintext, array $options = []);
/**
* Check that the password handler is supported in this environment
*
* @return boolean
*
* @since 1.2.0
*/
public static function isSupported();
/**
* Validate a password
*
* @param string $plaintext The plain text password to validate
* @param string $hashed The password hash to validate against
*
* @return boolean
*
* @since 1.2.0
*/
public function validatePassword($plaintext, $hashed);
}

View File

@ -0,0 +1,123 @@
<?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;
}
}

View File

@ -0,0 +1,91 @@
<?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\Input\Input;
/**
* Joomla Framework Local Strategy Authentication class
*
* @since 1.0
*/
class LocalStrategy extends AbstractUsernamePasswordAuthenticationStrategy
{
/**
* The credential store.
*
* @var array
* @since 1.0
*/
private $credentialStore;
/**
* The Input object
*
* @var Input
* @since 1.0
*/
private $input;
/**
* Strategy Constructor
*
* @param Input $input The input object from which to retrieve the request credentials.
* @param array $credentialStore Hash of username and hash pairs.
* @param ?HandlerInterface $passwordHandler The password handler.
*
* @since 1.0
*/
public function __construct(Input $input, array $credentialStore = [], ?HandlerInterface $passwordHandler = null)
{
parent::__construct($passwordHandler);
$this->credentialStore = $credentialStore;
$this->input = $input;
}
/**
* Attempt to authenticate the username and password pair.
*
* @return string|boolean A string containing a username if authentication is successful, false otherwise.
*
* @since 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)
{
return $this->credentialStore[$username] ?? false;
}
}