forked from wallabag/wallabag
Move source files directly under src/ directory
This commit is contained in:
34
src/SiteConfig/ArraySiteConfigBuilder.php
Normal file
34
src/SiteConfig/ArraySiteConfigBuilder.php
Normal 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;
|
||||
}
|
||||
}
|
||||
31
src/SiteConfig/Authenticator/Authenticator.php
Normal file
31
src/SiteConfig/Authenticator/Authenticator.php
Normal 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);
|
||||
}
|
||||
21
src/SiteConfig/Authenticator/Factory.php
Normal file
21
src/SiteConfig/Authenticator/Factory.php
Normal 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);
|
||||
}
|
||||
}
|
||||
112
src/SiteConfig/Authenticator/LoginFormAuthenticator.php
Normal file
112
src/SiteConfig/Authenticator/LoginFormAuthenticator.php
Normal 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)]
|
||||
);
|
||||
}
|
||||
}
|
||||
136
src/SiteConfig/GrabySiteConfigBuilder.php
Normal file
136
src/SiteConfig/GrabySiteConfigBuilder.php
Normal 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;
|
||||
}
|
||||
}
|
||||
263
src/SiteConfig/SiteConfig.php
Normal file
263
src/SiteConfig/SiteConfig.php
Normal 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;
|
||||
}
|
||||
}
|
||||
17
src/SiteConfig/SiteConfigBuilder.php
Normal file
17
src/SiteConfig/SiteConfigBuilder.php
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user