Move source files directly under src/ directory

This commit is contained in:
Yassine Guedidi
2024-02-19 00:39:48 +01:00
parent 804261bc26
commit a37b385c23
190 changed files with 19 additions and 21 deletions

View File

@ -0,0 +1,34 @@
<?php
namespace Wallabag\CoreBundle\SiteConfig;
class ArraySiteConfigBuilder implements SiteConfigBuilder
{
/**
* Map of hostname => SiteConfig.
*/
private $configs = [];
public function __construct(array $hostConfigMap = [])
{
foreach ($hostConfigMap as $host => $hostConfig) {
$hostConfig['host'] = $host;
$this->configs[$host] = new SiteConfig($hostConfig);
}
}
public function buildForHost($host)
{
$host = strtolower($host);
if ('www.' === substr($host, 0, 4)) {
$host = substr($host, 4);
}
if (isset($this->configs[$host])) {
return $this->configs[$host];
}
return false;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Wallabag\CoreBundle\SiteConfig\Authenticator;
use GuzzleHttp\ClientInterface;
interface Authenticator
{
/**
* Logs the configured user on the given Guzzle client.
*
* @return self
*/
public function login(ClientInterface $guzzle);
/**
* Checks if we are logged into the site, but without calling the server (e.g. do we have a Cookie).
*
* @return bool
*/
public function isLoggedIn(ClientInterface $guzzle);
/**
* Checks from the HTML of a page if authentication is requested by a grabbed page.
*
* @param string $html
*
* @return bool
*/
public function isLoginRequired($html);
}

View File

@ -0,0 +1,21 @@
<?php
namespace Wallabag\CoreBundle\SiteConfig\Authenticator;
use Wallabag\CoreBundle\SiteConfig\SiteConfig;
/**
* Builds an Authenticator based on a SiteConfig.
*/
class Factory
{
/**
* @return Authenticator
*
* @throw \OutOfRangeException if there are no credentials for this host
*/
public function buildFromSiteConfig(SiteConfig $siteConfig)
{
return new LoginFormAuthenticator($siteConfig);
}
}

View File

@ -0,0 +1,112 @@
<?php
namespace Wallabag\CoreBundle\SiteConfig\Authenticator;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Cookie\CookieJar;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Wallabag\CoreBundle\ExpressionLanguage\AuthenticatorProvider;
use Wallabag\CoreBundle\SiteConfig\SiteConfig;
class LoginFormAuthenticator implements Authenticator
{
/** @var \GuzzleHttp\Client */
protected $guzzle;
/** @var SiteConfig */
private $siteConfig;
public function __construct(SiteConfig $siteConfig)
{
// @todo OptionResolver
$this->siteConfig = $siteConfig;
}
public function login(ClientInterface $guzzle)
{
$postFields = [
$this->siteConfig->getUsernameField() => $this->siteConfig->getUsername(),
$this->siteConfig->getPasswordField() => $this->siteConfig->getPassword(),
] + $this->getExtraFields($guzzle);
$guzzle->post(
$this->siteConfig->getLoginUri(),
['body' => $postFields, 'allow_redirects' => true, 'verify' => false]
);
return $this;
}
public function isLoggedIn(ClientInterface $guzzle)
{
if (($cookieJar = $guzzle->getDefaultOption('cookies')) instanceof CookieJar) {
/** @var \GuzzleHttp\Cookie\SetCookie $cookie */
foreach ($cookieJar as $cookie) {
// check required cookies
if ($cookie->getDomain() === $this->siteConfig->getHost()) {
return true;
}
}
}
return false;
}
public function isLoginRequired($html)
{
$useInternalErrors = libxml_use_internal_errors(true);
// need to check for the login dom element ($options['not_logged_in_xpath']) in the HTML
$doc = new \DOMDocument();
$doc->loadHTML($html);
$xpath = new \DOMXPath($doc);
$loggedIn = $xpath->evaluate((string) $this->siteConfig->getNotLoggedInXpath());
if (false === $loggedIn) {
return false;
}
libxml_use_internal_errors($useInternalErrors);
return $loggedIn->length > 0;
}
/**
* Returns extra fields from the configuration.
* Evaluates any field value that is an expression language string.
*
* @return array
*/
private function getExtraFields(ClientInterface $guzzle)
{
$extraFields = [];
foreach ($this->siteConfig->getExtraFields() as $fieldName => $fieldValue) {
if ('@=' === substr($fieldValue, 0, 2)) {
$expressionLanguage = $this->getExpressionLanguage($guzzle);
$fieldValue = $expressionLanguage->evaluate(
substr($fieldValue, 2),
[
'config' => $this->siteConfig,
]
);
}
$extraFields[$fieldName] = $fieldValue;
}
return $extraFields;
}
/**
* @return ExpressionLanguage
*/
private function getExpressionLanguage(ClientInterface $guzzle)
{
return new ExpressionLanguage(
null,
[new AuthenticatorProvider($guzzle)]
);
}
}

View File

@ -0,0 +1,136 @@
<?php
namespace Wallabag\CoreBundle\SiteConfig;
use Graby\SiteConfig\ConfigBuilder;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Wallabag\CoreBundle\Repository\SiteCredentialRepository;
class GrabySiteConfigBuilder implements SiteConfigBuilder
{
/**
* @var ConfigBuilder
*/
private $grabyConfigBuilder;
/**
* @var SiteCredentialRepository
*/
private $credentialRepository;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var TokenStorageInterface
*/
private $token;
/**
* GrabySiteConfigBuilder constructor.
*/
public function __construct(ConfigBuilder $grabyConfigBuilder, TokenStorageInterface $token, SiteCredentialRepository $credentialRepository, LoggerInterface $logger)
{
$this->grabyConfigBuilder = $grabyConfigBuilder;
$this->credentialRepository = $credentialRepository;
$this->logger = $logger;
$this->token = $token;
}
public function buildForHost($host)
{
$user = $this->getUser();
// required by credentials below
$host = strtolower($host);
if ('www.' === substr($host, 0, 4)) {
$host = substr($host, 4);
}
if (!$user) {
$this->logger->debug('Auth: no current user defined.');
return false;
}
$hosts = [$host];
// will try to see for a host without the first subdomain (fr.example.org & .example.org)
$split = explode('.', $host);
if (\count($split) > 1) {
// remove first subdomain
array_shift($split);
$hosts[] = '.' . implode('.', $split);
}
$credentials = $this->credentialRepository->findOneByHostsAndUser($hosts, $user->getId());
if (null === $credentials) {
$this->logger->debug('Auth: no credentials available for host.', ['host' => $host]);
return false;
}
$config = $this->grabyConfigBuilder->buildForHost($host);
$parameters = [
'host' => $host,
'requiresLogin' => $config->requires_login ?: false,
'loginUri' => $config->login_uri ?: null,
'usernameField' => $config->login_username_field ?: null,
'passwordField' => $config->login_password_field ?: null,
'extraFields' => $this->processExtraFields($config->login_extra_fields),
'notLoggedInXpath' => $config->not_logged_in_xpath ?: null,
'username' => $credentials['username'],
'password' => $credentials['password'],
];
$config = new SiteConfig($parameters);
// do not leak usernames and passwords in log
$parameters['username'] = '**masked**';
$parameters['password'] = '**masked**';
$this->logger->debug('Auth: add parameters.', ['host' => $host, 'parameters' => $parameters]);
return $config;
}
/**
* Processes login_extra_fields config, transforming an '=' separated array of strings
* into a key/value array.
*
* @param array|mixed $extraFieldsStrings
*
* @return array
*/
protected function processExtraFields($extraFieldsStrings)
{
if (!\is_array($extraFieldsStrings)) {
return [];
}
$extraFields = [];
foreach ($extraFieldsStrings as $extraField) {
if (!str_contains($extraField, '=')) {
continue;
}
list($fieldName, $fieldValue) = explode('=', $extraField, 2);
$extraFields[$fieldName] = $fieldValue;
}
return $extraFields;
}
private function getUser()
{
if ($this->token->getToken() && null !== $this->token->getToken()->getUser()) {
return $this->token->getToken()->getUser();
}
return null;
}
}

View File

@ -0,0 +1,263 @@
<?php
namespace Wallabag\CoreBundle\SiteConfig;
/**
* Authentication configuration for a site.
*/
class SiteConfig
{
/**
* The site's host name.
*
* @var string
*/
protected $host;
/**
* If the site requires a loogin or not.
*
* @var bool
*/
protected $requiresLogin;
/**
* XPath query used to check if the user was logged in or not.
*
* @var string
*/
protected $notLoggedInXpath;
/**
* URI login data must be sent to.
*
* @var string
*/
protected $loginUri;
/**
* Name of the username field.
*
* @var string
*/
protected $usernameField;
/**
* Name of the password field.
*
* @var string
*/
protected $passwordField;
/**
* Associative array of extra fields to send with the form.
*
* @var array
*/
protected $extraFields = [];
/**
* Username to use for login.
*
* @var string
*/
protected $username;
/**
* Password to use for login.
*
* @var string
*/
protected $password;
/**
* SiteConfig constructor. Sets the properties by name given a hash.
*
* @throws \InvalidArgumentException if a property doesn't exist
*/
public function __construct(array $properties = [])
{
foreach ($properties as $propertyName => $propertyValue) {
if (!property_exists($this, $propertyName)) {
throw new \InvalidArgumentException('Unknown property: "' . $propertyName . '"');
}
$this->$propertyName = $propertyValue;
}
}
/**
* @return bool
*/
public function requiresLogin()
{
return $this->requiresLogin;
}
/**
* @param bool $requiresLogin
*
* @return SiteConfig
*/
public function setRequiresLogin($requiresLogin)
{
$this->requiresLogin = $requiresLogin;
return $this;
}
/**
* @return string
*/
public function getNotLoggedInXpath()
{
return $this->notLoggedInXpath;
}
/**
* @param string $notLoggedInXpath
*
* @return SiteConfig
*/
public function setNotLoggedInXpath($notLoggedInXpath)
{
$this->notLoggedInXpath = $notLoggedInXpath;
return $this;
}
/**
* @return string
*/
public function getLoginUri()
{
return $this->loginUri;
}
/**
* @param string $loginUri
*
* @return SiteConfig
*/
public function setLoginUri($loginUri)
{
$this->loginUri = $loginUri;
return $this;
}
/**
* @return string
*/
public function getUsernameField()
{
return $this->usernameField;
}
/**
* @param string $usernameField
*
* @return SiteConfig
*/
public function setUsernameField($usernameField)
{
$this->usernameField = $usernameField;
return $this;
}
/**
* @return string
*/
public function getPasswordField()
{
return $this->passwordField;
}
/**
* @param string $passwordField
*
* @return SiteConfig
*/
public function setPasswordField($passwordField)
{
$this->passwordField = $passwordField;
return $this;
}
/**
* @return array
*/
public function getExtraFields()
{
return $this->extraFields;
}
/**
* @param array $extraFields
*
* @return SiteConfig
*/
public function setExtraFields($extraFields)
{
$this->extraFields = $extraFields;
return $this;
}
/**
* @return string
*/
public function getHost()
{
return $this->host;
}
/**
* @param string $host
*
* @return SiteConfig
*/
public function setHost($host)
{
$this->host = $host;
return $this;
}
public function getUsername()
{
return $this->username;
}
/**
* @return SiteConfig
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* @param string $password
*
* @return SiteConfig
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Wallabag\CoreBundle\SiteConfig;
interface SiteConfigBuilder
{
/**
* Builds the SiteConfig for a host.
*
* @param string $host The "www." prefix is ignored.
*
* @throws \OutOfRangeException If there is no config for $host
*
* @return SiteConfig|false
*/
public function buildForHost($host);
}