Make AuthenticatorProvider use Symfony HTTP Client

This commit is contained in:
Yassine Guedidi 2024-12-23 00:02:59 +01:00
parent c2197bd020
commit 1e1d58da7f
5 changed files with 47 additions and 20 deletions

View file

@ -45,6 +45,8 @@ framework:
headers: headers:
Content-Type: 'application/json' Content-Type: 'application/json'
X-Accept: 'application/json' X-Accept: 'application/json'
request_html_function.client:
scope: '.*'
# Twig Configuration # Twig Configuration
twig: twig:

View file

@ -34,7 +34,7 @@ services:
Wallabag\: Wallabag\:
resource: '../../src/*' resource: '../../src/*'
exclude: ['../../src/{Consumer,Controller,Entity,ExpressionLanguage,DataFixtures,Redis}', '../../src/Event/*Event.php'] exclude: ['../../src/{Consumer,Controller,Entity,DataFixtures,Redis}', '../../src/Event/*Event.php']
# controllers are imported separately to make sure services can be injected # controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class # as action arguments even if you don't extend any base controller class

View file

@ -2,21 +2,18 @@
namespace Wallabag\ExpressionLanguage; namespace Wallabag\ExpressionLanguage;
use GuzzleHttp\ClientInterface;
use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunction;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class AuthenticatorProvider implements ExpressionFunctionProviderInterface class AuthenticatorProvider implements ExpressionFunctionProviderInterface
{ {
/** private HttpClientInterface $requestHtmlFunctionClient;
* @var ClientInterface
*/
private $guzzle;
public function __construct(ClientInterface $guzzle) public function __construct(HttpClientInterface $requestHtmlFunctionClient)
{ {
$this->guzzle = $guzzle; $this->requestHtmlFunctionClient = $requestHtmlFunctionClient;
} }
public function getFunctions(): array public function getFunctions(): array
@ -38,7 +35,7 @@ class AuthenticatorProvider implements ExpressionFunctionProviderInterface
throw new \Exception('Not supported'); throw new \Exception('Not supported');
}, },
function (array $arguments, $uri) { function (array $arguments, $uri) {
return $this->guzzle->get($uri, $options)->getBody(); return $this->requestHtmlFunctionClient->request('GET', $uri)->getContent();
} }
); );
} }

View file

@ -10,6 +10,13 @@ use Wallabag\ExpressionLanguage\AuthenticatorProvider;
class LoginFormAuthenticator class LoginFormAuthenticator
{ {
private AuthenticatorProvider $authenticatorProvider;
public function __construct(AuthenticatorProvider $authenticatorProvider)
{
$this->authenticatorProvider = $authenticatorProvider;
}
/** /**
* Logs the configured user on the given Guzzle client. * Logs the configured user on the given Guzzle client.
* *
@ -20,7 +27,7 @@ class LoginFormAuthenticator
$postFields = [ $postFields = [
$siteConfig->getUsernameField() => $siteConfig->getUsername(), $siteConfig->getUsernameField() => $siteConfig->getUsername(),
$siteConfig->getPasswordField() => $siteConfig->getPassword(), $siteConfig->getPasswordField() => $siteConfig->getPassword(),
] + $this->getExtraFields($siteConfig, $guzzle); ] + $this->getExtraFields($siteConfig);
$guzzle->post( $guzzle->post(
$siteConfig->getLoginUri(), $siteConfig->getLoginUri(),
@ -77,13 +84,13 @@ class LoginFormAuthenticator
* *
* @return array * @return array
*/ */
private function getExtraFields(SiteConfig $siteConfig, ClientInterface $guzzle) private function getExtraFields(SiteConfig $siteConfig)
{ {
$extraFields = []; $extraFields = [];
foreach ($siteConfig->getExtraFields() as $fieldName => $fieldValue) { foreach ($siteConfig->getExtraFields() as $fieldName => $fieldValue) {
if ('@=' === substr($fieldValue, 0, 2)) { if ('@=' === substr($fieldValue, 0, 2)) {
$expressionLanguage = $this->getExpressionLanguage($guzzle); $expressionLanguage = $this->getExpressionLanguage();
$fieldValue = $expressionLanguage->evaluate( $fieldValue = $expressionLanguage->evaluate(
substr($fieldValue, 2), substr($fieldValue, 2),
[ [
@ -101,11 +108,11 @@ class LoginFormAuthenticator
/** /**
* @return ExpressionLanguage * @return ExpressionLanguage
*/ */
private function getExpressionLanguage(ClientInterface $guzzle) private function getExpressionLanguage()
{ {
return new ExpressionLanguage( return new ExpressionLanguage(
null, null,
[new AuthenticatorProvider($guzzle)] [$this->authenticatorProvider]
); );
} }
} }

View file

@ -7,6 +7,9 @@ use GuzzleHttp\Message\Response;
use GuzzleHttp\Stream\Stream; use GuzzleHttp\Stream\Stream;
use GuzzleHttp\Subscriber\Mock; use GuzzleHttp\Subscriber\Mock;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Wallabag\ExpressionLanguage\AuthenticatorProvider;
use Wallabag\SiteConfig\LoginFormAuthenticator; use Wallabag\SiteConfig\LoginFormAuthenticator;
use Wallabag\SiteConfig\SiteConfig; use Wallabag\SiteConfig\SiteConfig;
@ -22,6 +25,8 @@ class LoginFormAuthenticatorTest extends TestCase
$guzzle = new Client(); $guzzle = new Client();
$guzzle->getEmitter()->attach(new Mock([$response])); $guzzle->getEmitter()->attach(new Mock([$response]));
$mockHttpClient = new MockHttpClient([new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]);
$siteConfig = new SiteConfig([ $siteConfig = new SiteConfig([
'host' => 'example.com', 'host' => 'example.com',
'loginUri' => 'http://example.com/login', 'loginUri' => 'http://example.com/login',
@ -35,7 +40,8 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$auth = new LoginFormAuthenticator(); $authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$res = $auth->login($siteConfig, $guzzle); $res = $auth->login($siteConfig, $guzzle);
$this->assertInstanceOf(LoginFormAuthenticator::class, $res); $this->assertInstanceOf(LoginFormAuthenticator::class, $res);
@ -51,6 +57,8 @@ class LoginFormAuthenticatorTest extends TestCase
$guzzle = new Client(); $guzzle = new Client();
$guzzle->getEmitter()->attach(new Mock([$response, $response])); $guzzle->getEmitter()->attach(new Mock([$response, $response]));
$mockHttpClient = new MockHttpClient([new MockResponse('<html></html>', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]);
$siteConfig = new SiteConfig([ $siteConfig = new SiteConfig([
'host' => 'example.com', 'host' => 'example.com',
'loginUri' => 'http://example.com/login', 'loginUri' => 'http://example.com/login',
@ -65,7 +73,8 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$auth = new LoginFormAuthenticator(); $authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$res = $auth->login($siteConfig, $guzzle); $res = $auth->login($siteConfig, $guzzle);
$this->assertInstanceOf(LoginFormAuthenticator::class, $res); $this->assertInstanceOf(LoginFormAuthenticator::class, $res);
@ -86,6 +95,8 @@ class LoginFormAuthenticatorTest extends TestCase
->method('getStatusCode') ->method('getStatusCode')
->willReturn(200); ->willReturn(200);
$mockHttpClient = new MockHttpClient([new MockResponse(file_get_contents(__DIR__ . '/../fixtures/aoc.media.html'), ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]);
$client = $this->getMockBuilder(Client::class) $client = $this->getMockBuilder(Client::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
@ -128,7 +139,8 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$auth = new LoginFormAuthenticator(); $authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$res = $auth->login($siteConfig, $client); $res = $auth->login($siteConfig, $client);
$this->assertInstanceOf(LoginFormAuthenticator::class, $res); $this->assertInstanceOf(LoginFormAuthenticator::class, $res);
@ -148,6 +160,8 @@ class LoginFormAuthenticatorTest extends TestCase
->method('getStatusCode') ->method('getStatusCode')
->willReturn(200); ->willReturn(200);
$mockHttpClient = new MockHttpClient([new MockResponse(file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html'), ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]);
$client = $this->getMockBuilder(Client::class) $client = $this->getMockBuilder(Client::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
@ -194,7 +208,8 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$auth = new LoginFormAuthenticator(); $authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$res = $auth->login($siteConfig, $client); $res = $auth->login($siteConfig, $client);
$this->assertInstanceOf(LoginFormAuthenticator::class, $res); $this->assertInstanceOf(LoginFormAuthenticator::class, $res);
@ -210,7 +225,10 @@ class LoginFormAuthenticatorTest extends TestCase
'password' => 'unkn0wn', 'password' => 'unkn0wn',
]); ]);
$auth = new LoginFormAuthenticator(); $mockHttpClient = new MockHttpClient();
$authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html')); $loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html'));
$this->assertFalse($loginRequired); $this->assertFalse($loginRequired);
@ -227,7 +245,10 @@ class LoginFormAuthenticatorTest extends TestCase
'notLoggedInXpath' => '//h2[@class="title_reserve_article"]', 'notLoggedInXpath' => '//h2[@class="title_reserve_article"]',
]); ]);
$auth = new LoginFormAuthenticator(); $mockHttpClient = new MockHttpClient();
$authenticatorProvider = new AuthenticatorProvider($mockHttpClient);
$auth = new LoginFormAuthenticator($authenticatorProvider);
$loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-article.html')); $loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-article.html'));
$this->assertTrue($loginRequired); $this->assertTrue($loginRequired);