diff --git a/app/AppKernel.php b/app/AppKernel.php index 5fbe9ce25..6cafa9e95 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -32,7 +32,6 @@ class AppKernel extends Kernel new BabDev\PagerfantaBundle\BabDevPagerfantaBundle(), new FOS\JsRoutingBundle\FOSJsRoutingBundle(), new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(), - new Http\HttplugBundle\HttplugBundle(), new Sentry\SentryBundle\SentryBundle(), new Twig\Extra\TwigExtraBundle\TwigExtraBundle(), new Symfony\WebpackEncoreBundle\WebpackEncoreBundle(), diff --git a/app/config/config.yml b/app/config/config.yml index 6c90f2dfc..1d65c8de4 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -48,6 +48,10 @@ framework: X-Accept: 'application/json' request_html_function.client: scope: '.*' + browser.client: + scope: '.*' + verify_host: false + verify_peer: false # Twig Configuration twig: @@ -452,17 +456,6 @@ sensio_framework_extra: router: annotations: false -httplug: - clients: - wallabag: - factory: Wallabag\Helper\HttpClientFactory - config: - defaults: - timeout: 10 - plugins: ['httplug.plugin.logger'] - discovery: - client: false - # define custom entity so we can override length attribute to fix utf8mb4 issue craue_config: entity_name: Wallabag\Entity\InternalSetting diff --git a/app/config/services.yml b/app/config/services.yml index 662a858ef..09eb22729 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -191,11 +191,17 @@ services: tags: - { name: doctrine.event_subscriber } + psr18.wallabag.client: + class: Symfony\Component\HttpClient\Psr18Client + arguments: + $client: '@Wallabag\HttpClient\WallabagClient' + Graby\Graby: arguments: $config: error_message: '%wallabag.fetching_error_message%' error_message_title: '%wallabag.fetching_error_message_title%' + $client: '@psr18.wallabag.client' calls: - [ setLogger, [ "@logger" ] ] tags: @@ -205,9 +211,6 @@ services: arguments: $config: {} - wallabag.http_client: - alias: 'httplug.client.wallabag' - Wallabag\SiteConfig\GrabySiteConfigBuilder: tags: - { name: monolog.logger, channel: graby } @@ -216,11 +219,9 @@ services: Wallabag\SiteConfig\SiteConfigBuilder: alias: Wallabag\SiteConfig\GrabySiteConfigBuilder - GuzzleHttp\Cookie\CookieJar: ~ - - Wallabag\Helper\HttpClientFactory: - calls: - - ['addSubscriber', ['@Wallabag\Guzzle\AuthenticatorSubscriber']] + Symfony\Component\BrowserKit\HttpBrowser: + arguments: + $client: '@browser.client' RulerZ\RulerZ: alias: rulerz diff --git a/composer-dependency-analyser.php b/composer-dependency-analyser.php index e0a9d659c..a83b8247d 100644 --- a/composer-dependency-analyser.php +++ b/composer-dependency-analyser.php @@ -29,8 +29,6 @@ $config 'mnapoli/piwik-twig-extension', 'ocramius/proxy-manager', 'pagerfanta/twig', - 'php-http/client-common', - 'php-http/httplug', 'php-http/mock-client', 'phpstan/extension-installer', 'phpstan/phpstan', @@ -39,13 +37,11 @@ $config 'phpstan/phpstan-symfony', 'psr/http-client', 'psr/http-factory', - 'psr/http-message', 'rulerz-php/doctrine-orm', 'scheb/2fa-qr-code', 'scheb/2fa-trusted-device', 'shipmonk/composer-dependency-analyser', 'symfony/asset', - 'symfony/browser-kit', 'symfony/css-selector', 'symfony/doctrine-bridge', 'symfony/google-mailer', @@ -57,10 +53,8 @@ $config 'twig/string-extra', ], [ErrorType::UNUSED_DEPENDENCY]) ->ignoreErrorsOnPackages([ - 'guzzlehttp/streams', 'monolog/monolog', 'symfony/filesystem', - 'symfony/http-client', ], [ErrorType::PROD_DEPENDENCY_ONLY_IN_DEV]) ->ignoreErrorsOnPackages([ 'dama/doctrine-test-bundle', diff --git a/composer.json b/composer.json index 72692e775..3a4839a6f 100644 --- a/composer.json +++ b/composer.json @@ -75,9 +75,7 @@ "friendsofsymfony/oauth-server-bundle": "dev-master#dc8ff343363cf794d30eb1a123610d186a43f162", "friendsofsymfony/rest-bundle": "^3.6", "friendsofsymfony/user-bundle": "^3.2.1", - "guzzlehttp/guzzle": "^5.3.4", "guzzlehttp/psr7": "^2.6.2", - "guzzlehttp/streams": "^3.0", "html2text/html2text": "^4.3.1", "incenteev/composer-parameter-handler": "^2.2", "j0k3r/graby": "^2.4.5", @@ -99,10 +97,6 @@ "pagerfanta/twig": "^3.8", "php-amqplib/php-amqplib": "^3.6.1", "php-amqplib/rabbitmq-bundle": "^2.14.0", - "php-http/client-common": "^2.7.1", - "php-http/guzzle5-adapter": "^2.0", - "php-http/httplug": "^2.4", - "php-http/httplug-bundle": "^1.32", "pragmarx/recovery": "^0.2.1", "predis/predis": "^2.2.2", "psr/http-client": "^1.0.3", @@ -122,6 +116,7 @@ "spiriitlabs/form-filter-bundle": "^10.0", "stof/doctrine-extensions-bundle": "^1.11.0", "symfony/asset": "^5.4.35", + "symfony/browser-kit": "^5.4.35", "symfony/config": "^5.4.35", "symfony/console": "^5.4.35", "symfony/dependency-injection": "^5.4.35", @@ -182,7 +177,6 @@ "phpstan/phpstan-symfony": "^1.3.7", "phpunit/phpunit": "^9.6.17", "shipmonk/composer-dependency-analyser": "^1.7", - "symfony/browser-kit": "^5.4.35", "symfony/css-selector": "^5.4.35", "symfony/debug-bundle": "^5.4.35", "symfony/maker-bundle": "^1.43", diff --git a/composer.lock b/composer.lock index 54f7eece1..523177337 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1f0877b1f0f7fc3b074786b51d772cb6", + "content-hash": "a85056bec7fa90b9be4aa16c34464c0e", "packages": [ { "name": "babdev/pagerfanta-bundle", @@ -2980,63 +2980,6 @@ }, "time": "2015-05-14T08:18:23+00:00" }, - { - "name": "guzzlehttp/guzzle", - "version": "5.3.4", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "b87eda7a7162f95574032da17e9323c9899cb6b2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b87eda7a7162f95574032da17e9323c9899cb6b2", - "reference": "b87eda7a7162f95574032da17e9323c9899cb6b2", - "shasum": "" - }, - "require": { - "guzzlehttp/ringphp": "^1.1", - "php": ">=5.4.0", - "react/promise": "^2.2" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/5.3" - }, - "time": "2019-10-30T09:32:00+00:00" - }, { "name": "guzzlehttp/psr7", "version": "2.7.0", @@ -3153,117 +3096,6 @@ ], "time": "2024-07-18T11:15:46+00:00" }, - { - "name": "guzzlehttp/ringphp", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/RingPHP.git", - "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/5e2a174052995663dd68e6b5ad838afd47dd615b", - "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b", - "shasum": "" - }, - "require": { - "guzzlehttp/streams": "~3.0", - "php": ">=5.4.0", - "react/promise": "~2.0" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Ring\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "support": { - "issues": "https://github.com/guzzle/RingPHP/issues", - "source": "https://github.com/guzzle/RingPHP/tree/1.1.1" - }, - "abandoned": true, - "time": "2018-07-31T13:22:33+00:00" - }, - { - "name": "guzzlehttp/streams", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/streams.git", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Stream\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "Guzzle", - "stream" - ], - "support": { - "issues": "https://github.com/guzzle/streams/issues", - "source": "https://github.com/guzzle/streams/tree/master" - }, - "abandoned": true, - "time": "2014-10-12T19:18:40+00:00" - }, { "name": "hoa/compiler", "version": "3.17.08.08", @@ -6511,74 +6343,6 @@ }, "time": "2024-10-02T11:20:13+00:00" }, - { - "name": "php-http/guzzle5-adapter", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/guzzle5-adapter.git", - "reference": "cce48360b1f8a3467bd94e853e6107aa4532008e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/guzzle5-adapter/zipball/cce48360b1f8a3467bd94e853e6107aa4532008e", - "reference": "cce48360b1f8a3467bd94e853e6107aa4532008e", - "shasum": "" - }, - "require": { - "guzzlehttp/guzzle": "^5.1", - "php": "^7.0", - "php-http/discovery": "^1.0", - "php-http/httplug": "^2.0" - }, - "provide": { - "php-http/client-implementation": "1.0", - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "ext-curl": "*", - "guzzlehttp/ringphp": "^1.1", - "php-http/client-integration-tests": "^2.0", - "phpunit/phpunit": "^6.0 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Adapter\\Guzzle5\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eric GELOEN", - "email": "geloen.eric@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Guzzle 5 HTTP Adapter", - "homepage": "http://httplug.io", - "keywords": [ - "Guzzle", - "http" - ], - "support": { - "issues": "https://github.com/php-http/guzzle5-adapter/issues", - "source": "https://github.com/php-http/guzzle5-adapter/tree/2.0.0" - }, - "abandoned": "php-http/guzzle7-adapter", - "time": "2019-02-05T12:28:45+00:00" - }, { "name": "php-http/httplug", "version": "2.4.1", @@ -6636,169 +6400,6 @@ }, "time": "2024-09-23T11:39:58+00:00" }, - { - "name": "php-http/httplug-bundle", - "version": "1.34.3", - "source": { - "type": "git", - "url": "https://github.com/php-http/HttplugBundle.git", - "reference": "87c61d27c025dd9d699a2208a6d06be58061e433" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/HttplugBundle/zipball/87c61d27c025dd9d699a2208a6d06be58061e433", - "reference": "87c61d27c025dd9d699a2208a6d06be58061e433", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8.0", - "php-http/client-common": "^1.9 || ^2.0", - "php-http/client-implementation": "^1.0", - "php-http/discovery": "^1.14", - "php-http/httplug": "^2.0", - "php-http/logger-plugin": "^1.1", - "php-http/message": "^1.13", - "php-http/message-factory": "^1.0.2", - "php-http/stopwatch-plugin": "^1.2", - "psr/http-message": "^1.0 || ^2.0", - "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/options-resolver": "^4.4 || ^5.0 || ^6.0 || ^7.0" - }, - "conflict": { - "php-http/cache-plugin": "<1.7.0", - "php-http/curl-client": "<2.0", - "php-http/guzzle6-adapter": "<1.1", - "php-http/socket-client": "<2.0", - "php-http/throttle-plugin": "<1.1" - }, - "require-dev": { - "guzzlehttp/psr7": "^1.7 || ^2.0", - "matthiasnoback/symfony-config-test": "^4.3 || ^5.0", - "matthiasnoback/symfony-dependency-injection-test": "^4.3.1 || ^5.0", - "nyholm/nsa": "^1.1", - "nyholm/psr7": "^1.2.1", - "php-http/cache-plugin": "^1.7", - "php-http/mock-client": "^1.2", - "php-http/promise": "^1.0", - "phpunit/phpunit": "^9.6", - "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/cache": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/dom-crawler": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/http-foundation": "^4.4.19 || ^5.0 || ^6.0 || ^7.0", - "symfony/stopwatch": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/twig-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/web-profiler-bundle": "^4.4.19 || ^5.0 || ^6.0 || ^7.0", - "twig/twig": "^1.41 || ^2.10 || ^3.0" - }, - "suggest": { - "php-http/cache-plugin": "To configure clients that cache responses", - "php-http/mock-client": "Add this to your require-dev section to mock HTTP responses easily", - "twig/twig": "Add this to your require-dev section when using the WebProfilerBundle" - }, - "type": "symfony-bundle", - "autoload": { - "psr-4": { - "Http\\HttplugBundle\\": "src/" - }, - "exclude-from-classmap": [ - "/Tests/Resources/MyPsr18TestClient.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "David Buchmann", - "email": "mail@davidbu.ch" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com" - } - ], - "description": "Symfony integration for HTTPlug", - "homepage": "http://httplug.io", - "keywords": [ - "adapter", - "bundle", - "discovery", - "factory", - "http", - "httplug", - "message", - "php-http" - ], - "support": { - "issues": "https://github.com/php-http/HttplugBundle/issues", - "source": "https://github.com/php-http/HttplugBundle/tree/1.34.3" - }, - "time": "2024-09-01T08:25:40+00:00" - }, - { - "name": "php-http/logger-plugin", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/php-http/logger-plugin.git", - "reference": "bf47eb5cb379962d276c94da14861669c2313563" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/logger-plugin/zipball/bf47eb5cb379962d276c94da14861669c2313563", - "reference": "bf47eb5cb379962d276c94da14861669c2313563", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "php-http/client-common": "^1.9 || ^2.0", - "php-http/message": "^1.0", - "psr/log": "^1.0 || ^2 || ^3", - "symfony/polyfill-php73": "^1.17" - }, - "require-dev": { - "phpspec/phpspec": "^5.1 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Client\\Common\\Plugin\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "PSR-3 Logger plugin for HTTPlug", - "homepage": "http://httplug.io", - "keywords": [ - "http", - "httplug", - "logger", - "plugin" - ], - "support": { - "issues": "https://github.com/php-http/logger-plugin/issues", - "source": "https://github.com/php-http/logger-plugin/tree/1.3.1" - }, - "time": "2024-09-01T06:51:51+00:00" - }, { "name": "php-http/message", "version": "1.16.2", @@ -6975,64 +6576,6 @@ }, "time": "2024-03-15T13:55:21+00:00" }, - { - "name": "php-http/stopwatch-plugin", - "version": "1.4.2", - "source": { - "type": "git", - "url": "https://github.com/php-http/stopwatch-plugin.git", - "reference": "11862cfbc719afade4ff407964ab3fbfe9ec2baa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/stopwatch-plugin/zipball/11862cfbc719afade4ff407964ab3fbfe9ec2baa", - "reference": "11862cfbc719afade4ff407964ab3fbfe9ec2baa", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8.0", - "php-http/client-common": "^1.9 || ^2.0", - "symfony/stopwatch": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "require-dev": { - "guzzlehttp/psr7": "^2.1", - "symfony/phpunit-bridge": "^6.4.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Client\\Common\\Plugin\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Symfony Stopwatch plugin for HTTPlug", - "homepage": "http://httplug.io", - "keywords": [ - "http", - "httplug", - "plugin", - "stopwatch" - ], - "support": { - "issues": "https://github.com/php-http/stopwatch-plugin/issues", - "source": "https://github.com/php-http/stopwatch-plugin/tree/1.4.2" - }, - "time": "2023-12-05T14:36:57+00:00" - }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -8126,78 +7669,6 @@ }, "time": "2019-03-08T08:55:37+00:00" }, - { - "name": "react/promise", - "version": "v2.11.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/1a8460931ea36dc5c76838fec5734d55c88c6831", - "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "React\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "keywords": [ - "promise", - "promises" - ], - "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v2.11.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2023-11-16T16:16:50+00:00" - }, { "name": "rulerz-php/doctrine-orm", "version": "dev-master", @@ -9325,6 +8796,78 @@ ], "time": "2024-10-22T13:05:35+00:00" }, + { + "name": "symfony/browser-kit", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "03cce39764429e07fbab9b989a1182a24578341d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/03cce39764429e07fbab9b989a1182a24578341d", + "reference": "03cce39764429e07fbab9b989a1182a24578341d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/dom-crawler": "^4.4|^5.0|^6.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony/css-selector": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/mime": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v5.4.45" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-22T13:05:35+00:00" + }, { "name": "symfony/cache", "version": "v5.4.46", @@ -18533,6 +18076,78 @@ ], "time": "2023-11-13T13:48:05+00:00" }, + { + "name": "react/promise", + "version": "v2.11.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/1a8460931ea36dc5c76838fec5734d55c88c6831", + "reference": "1a8460931ea36dc5c76838fec5734d55c88c6831", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v2.11.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-16T16:16:50+00:00" + }, { "name": "react/socket", "version": "v1.16.0", @@ -19720,78 +19335,6 @@ }, "time": "2024-12-30T12:31:04+00:00" }, - { - "name": "symfony/browser-kit", - "version": "v5.4.45", - "source": { - "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "03cce39764429e07fbab9b989a1182a24578341d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/03cce39764429e07fbab9b989a1182a24578341d", - "reference": "03cce39764429e07fbab9b989a1182a24578341d", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/dom-crawler": "^4.4|^5.0|^6.0", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "symfony/css-selector": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/browser-kit/tree/v5.4.45" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-10-22T13:05:35+00:00" - }, { "name": "symfony/css-selector", "version": "v5.4.45", @@ -20407,9 +19950,9 @@ "ext-tokenizer": "*", "ext-xml": "*" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "7.4.29" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/Guzzle/AuthenticatorSubscriber.php b/src/Guzzle/AuthenticatorSubscriber.php deleted file mode 100644 index 4b565076e..000000000 --- a/src/Guzzle/AuthenticatorSubscriber.php +++ /dev/null @@ -1,121 +0,0 @@ -configBuilder = $configBuilder; - $this->authenticator = $authenticator; - $this->logger = new NullLogger(); - } - - public function setLogger(LoggerInterface $logger): void - { - $this->logger = $logger; - } - - public function getEvents(): array - { - return [ - 'before' => ['loginIfRequired'], - 'complete' => ['loginIfRequested'], - ]; - } - - public function loginIfRequired(BeforeEvent $event) - { - $config = $this->buildSiteConfig($event->getRequest()); - if (false === $config || !$config->requiresLogin()) { - $this->logger->debug('loginIfRequired> will not require login'); - - return; - } - - $client = $event->getClient(); - - if (!$this->authenticator->isLoggedIn($config, $client)) { - $this->logger->debug('loginIfRequired> user is not logged in, attach authenticator'); - - $emitter = $client->getEmitter(); - $emitter->detach($this); - $this->authenticator->login($config, $client); - $emitter->attach($this); - } - } - - public function loginIfRequested(CompleteEvent $event) - { - $config = $this->buildSiteConfig($event->getRequest()); - if (false === $config || !$config->requiresLogin()) { - $this->logger->debug('loginIfRequested> will not require login'); - - return; - } - - $body = $event->getResponse()->getBody(); - - if ( - null === $body - || '' === $body->getContents() - ) { - $this->logger->debug('loginIfRequested> empty body, ignoring'); - - return; - } - - $isLoginRequired = $this->authenticator->isLoginRequired($config, $body); - - $this->logger->debug('loginIfRequested> retry #' . $this->retries . ' with login ' . ($isLoginRequired ? '' : 'not ') . 'required'); - - if ($isLoginRequired && $this->retries < self::MAX_RETRIES) { - $client = $event->getClient(); - - $emitter = $client->getEmitter(); - $emitter->detach($this); - $this->authenticator->login($config, $client); - $emitter->attach($this); - - $event->retry(); - - ++$this->retries; - } - } - - /** - * @return SiteConfig|false - */ - private function buildSiteConfig(RequestInterface $request) - { - return $this->configBuilder->buildForHost($request->getHost()); - } -} diff --git a/src/Helper/HttpClientFactory.php b/src/Helper/HttpClientFactory.php deleted file mode 100644 index 7a5f6a5a6..000000000 --- a/src/Helper/HttpClientFactory.php +++ /dev/null @@ -1,74 +0,0 @@ -cookieJar = $cookieJar; - $this->restrictedAccess = $restrictedAccess; - $this->logger = $logger; - } - - /** - * Adds a subscriber to the HTTP client. - */ - public function addSubscriber(SubscriberInterface $subscriber) - { - $this->subscribers[] = $subscriber; - } - - /** - * Input an array of configuration to be able to create a HttpClient. - * - * @return HttpClient - */ - public function createClient(array $config = []) - { - $this->logger->log('debug', 'Restricted access config enabled?', ['enabled' => (int) $this->restrictedAccess]); - - if (0 === (int) $this->restrictedAccess) { - return new GuzzleAdapter(new GuzzleClient($config)); - } - - // we clear the cookie to avoid websites who use cookies for analytics - $this->cookieJar->clear(); - if (!isset($config['defaults']['cookies'])) { - // need to set the (shared) cookie jar - $config['defaults']['cookies'] = $this->cookieJar; - } - - $guzzle = new GuzzleClient($config); - foreach ($this->subscribers as $subscriber) { - $guzzle->getEmitter()->attach($subscriber); - } - - return new GuzzleAdapter($guzzle); - } -} diff --git a/src/HttpClient/Authenticator.php b/src/HttpClient/Authenticator.php new file mode 100644 index 000000000..47a7a17aa --- /dev/null +++ b/src/HttpClient/Authenticator.php @@ -0,0 +1,95 @@ +configBuilder = $configBuilder; + $this->authenticator = $authenticator; + $this->logger = new NullLogger(); + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + + public function loginIfRequired(string $url): bool + { + $config = $this->buildSiteConfig(new Uri($url)); + if (false === $config || !$config->requiresLogin()) { + $this->logger->debug('loginIfRequired> will not require login'); + + return false; + } + + if ($this->authenticator->isLoggedIn($config)) { + return false; + } + + $this->logger->debug('loginIfRequired> user is not logged in, attach authenticator'); + + $this->authenticator->login($config); + + return true; + } + + public function loginIfRequested(ResponseInterface $response): bool + { + $config = $this->buildSiteConfig(new Uri($response->getInfo('url'))); + if (false === $config || !$config->requiresLogin()) { + $this->logger->debug('loginIfRequested> will not require login'); + + return false; + } + + $body = $response->getContent(); + + if ('' === $body) { + $this->logger->debug('loginIfRequested> empty body, ignoring'); + + return false; + } + + $isLoginRequired = $this->authenticator->isLoginRequired($config, $body); + + $this->logger->debug('loginIfRequested> retry with login ' . ($isLoginRequired ? '' : 'not ') . 'required'); + + if (!$isLoginRequired) { + return false; + } + + $this->authenticator->login($config); + + return true; + } + + /** + * @return SiteConfig|false + */ + private function buildSiteConfig(UriInterface $uri) + { + return $this->configBuilder->buildForHost($uri->getHost()); + } +} diff --git a/src/HttpClient/WallabagClient.php b/src/HttpClient/WallabagClient.php new file mode 100644 index 000000000..1d3fcec6d --- /dev/null +++ b/src/HttpClient/WallabagClient.php @@ -0,0 +1,84 @@ +restrictedAccess = $restrictedAccess; + $this->browser = $browser; + $this->authenticator = $authenticator; + $this->logger = $logger; + + $this->httpClient = HttpClient::create([ + 'timeout' => 10, + ]); + } + + public function request(string $method, string $url, array $options = []): ResponseInterface + { + $this->logger->log('debug', 'Restricted access config enabled?', ['enabled' => (int) $this->restrictedAccess]); + + if (0 === (int) $this->restrictedAccess) { + return $this->httpClient->request($method, $url, $options); + } + + $login = $this->authenticator->loginIfRequired($url); + + if (!$login) { + return $this->httpClient->request($method, $url, $options); + } + + if (null !== $cookieHeader = $this->getCookieHeader($url)) { + $options['headers']['cookie'] = $cookieHeader; + } + + $response = $this->httpClient->request($method, $url, $options); + + $login = $this->authenticator->loginIfRequested($response); + + if (!$login) { + return $response; + } + + if (null !== $cookieHeader = $this->getCookieHeader($url)) { + $options['headers']['cookie'] = $cookieHeader; + } + + return $this->httpClient->request($method, $url, $options); + } + + public function stream($responses, ?float $timeout = null): ResponseStreamInterface + { + return $this->httpClient->stream($responses, $timeout); + } + + private function getCookieHeader(string $url): ?string + { + $cookies = []; + + foreach ($this->browser->getCookieJar()->allRawValues($url) as $name => $value) { + $cookies[] = $name . '=' . $value; + } + + if ([] === $cookies) { + return null; + } + + return implode('; ', $cookies); + } +} diff --git a/src/SiteConfig/LoginFormAuthenticator.php b/src/SiteConfig/LoginFormAuthenticator.php index b3ebbd5d1..393d3d8cf 100644 --- a/src/SiteConfig/LoginFormAuthenticator.php +++ b/src/SiteConfig/LoginFormAuthenticator.php @@ -2,18 +2,19 @@ namespace Wallabag\SiteConfig; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\Cookie\CookieJar; +use Symfony\Component\BrowserKit\HttpBrowser; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Wallabag\ExpressionLanguage\AuthenticatorProvider; class LoginFormAuthenticator { + private HttpBrowser $browser; private ExpressionLanguage $expressionLanguage; - public function __construct(AuthenticatorProvider $authenticatorProvider) + public function __construct(HttpBrowser $browser, AuthenticatorProvider $authenticatorProvider) { + $this->browser = $browser; $this->expressionLanguage = new ExpressionLanguage(null, [$authenticatorProvider]); } @@ -22,17 +23,14 @@ class LoginFormAuthenticator * * @return self */ - public function login(SiteConfig $siteConfig, ClientInterface $guzzle) + public function login(SiteConfig $siteConfig) { $postFields = [ $siteConfig->getUsernameField() => $siteConfig->getUsername(), $siteConfig->getPasswordField() => $siteConfig->getPassword(), ] + $this->getExtraFields($siteConfig); - $guzzle->post( - $siteConfig->getLoginUri(), - ['body' => $postFields, 'allow_redirects' => true, 'verify' => false] - ); + $this->browser->request('POST', $siteConfig->getLoginUri(), $postFields); return $this; } @@ -42,15 +40,12 @@ class LoginFormAuthenticator * * @return bool */ - public function isLoggedIn(SiteConfig $siteConfig, ClientInterface $guzzle) + public function isLoggedIn(SiteConfig $siteConfig) { - if (($cookieJar = $guzzle->getDefaultOption('cookies')) instanceof CookieJar) { - /** @var \GuzzleHttp\Cookie\SetCookie $cookie */ - foreach ($cookieJar as $cookie) { - // check required cookies - if ($cookie->getDomain() === $siteConfig->getHost()) { - return true; - } + foreach ($this->browser->getCookieJar()->all() as $cookie) { + // check required cookies + if ($cookie->getDomain() === $siteConfig->getHost()) { + return true; } } diff --git a/tests/Guzzle/AuthenticatorSubscriberTest.php b/tests/Guzzle/AuthenticatorSubscriberTest.php deleted file mode 100644 index 8ffb99208..000000000 --- a/tests/Guzzle/AuthenticatorSubscriberTest.php +++ /dev/null @@ -1,314 +0,0 @@ -getMockBuilder(LoginFormAuthenticator::class) - ->disableOriginalConstructor() - ->getMock(); - - $subscriber = new AuthenticatorSubscriber( - new ArraySiteConfigBuilder(), - $authenticator - ); - $events = $subscriber->getEvents(); - - $this->assertArrayHasKey('before', $events); - $this->assertArrayHasKey('complete', $events); - $this->assertSame('loginIfRequired', $events['before'][0]); - $this->assertSame('loginIfRequested', $events['complete'][0]); - } - - public function testLoginIfRequiredNotRequired() - { - $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) - ->disableOriginalConstructor() - ->getMock(); - - $builder = new ArraySiteConfigBuilder(['example.com' => []]); - $subscriber = new AuthenticatorSubscriber($builder, $authenticator); - - $logger = new Logger('foo'); - $handler = new TestHandler(); - $logger->pushHandler($handler); - - $subscriber->setLogger($logger); - - $request = new Request('GET', 'http://www.example.com'); - - $event = $this->getMockBuilder(BeforeEvent::class) - ->disableOriginalConstructor() - ->getMock(); - - $event->expects($this->once()) - ->method('getRequest') - ->willReturn($request); - - $subscriber->loginIfRequired($event); - - $records = $handler->getRecords(); - - $this->assertCount(1, $records); - $this->assertSame('loginIfRequired> will not require login', $records[0]['message']); - } - - public function testLoginIfRequiredWithNotLoggedInUser() - { - $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) - ->disableOriginalConstructor() - ->getMock(); - - $authenticator->expects($this->once()) - ->method('isLoggedIn') - ->willReturn(false); - - $authenticator->expects($this->once()) - ->method('login'); - - $builder = new ArraySiteConfigBuilder(['example.com' => ['requiresLogin' => true]]); - $subscriber = new AuthenticatorSubscriber($builder, $authenticator); - - $logger = new Logger('foo'); - $handler = new TestHandler(); - $logger->pushHandler($handler); - - $subscriber->setLogger($logger); - - $response = new Response( - 200, - ['content-type' => 'text/html'], - Stream::factory('') - ); - $guzzle = new Client(); - $guzzle->getEmitter()->attach(new Mock([$response])); - - $request = new Request('GET', 'http://www.example.com'); - - $event = $this->getMockBuilder(BeforeEvent::class) - ->disableOriginalConstructor() - ->getMock(); - - $event->expects($this->once()) - ->method('getRequest') - ->willReturn($request); - - $event->expects($this->once()) - ->method('getClient') - ->willReturn($guzzle); - - $subscriber->loginIfRequired($event); - - $records = $handler->getRecords(); - - $this->assertCount(1, $records); - $this->assertSame('loginIfRequired> user is not logged in, attach authenticator', $records[0]['message']); - } - - public function testLoginIfRequestedNotRequired() - { - $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) - ->disableOriginalConstructor() - ->getMock(); - - $builder = new ArraySiteConfigBuilder(['example.com' => []]); - $subscriber = new AuthenticatorSubscriber($builder, $authenticator); - - $logger = new Logger('foo'); - $handler = new TestHandler(); - $logger->pushHandler($handler); - - $subscriber->setLogger($logger); - - $request = new Request('GET', 'http://www.example.com'); - - $event = $this->getMockBuilder(CompleteEvent::class) - ->disableOriginalConstructor() - ->getMock(); - - $event->expects($this->once()) - ->method('getRequest') - ->willReturn($request); - - $subscriber->loginIfRequested($event); - - $records = $handler->getRecords(); - - $this->assertCount(1, $records); - $this->assertSame('loginIfRequested> will not require login', $records[0]['message']); - } - - public function testLoginIfRequestedNotRequested() - { - $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) - ->disableOriginalConstructor() - ->getMock(); - - $authenticator->expects($this->once()) - ->method('isLoginRequired') - ->willReturn(false); - - $builder = new ArraySiteConfigBuilder(['example.com' => [ - 'requiresLogin' => true, - 'notLoggedInXpath' => '//html', - ]]); - $subscriber = new AuthenticatorSubscriber($builder, $authenticator); - - $logger = new Logger('foo'); - $handler = new TestHandler(); - $logger->pushHandler($handler); - - $subscriber->setLogger($logger); - - $response = new Response( - 200, - ['content-type' => 'text/html'], - Stream::factory('') - ); - $request = new Request('GET', 'http://www.example.com'); - - $event = $this->getMockBuilder(CompleteEvent::class) - ->disableOriginalConstructor() - ->getMock(); - - $event->expects($this->once()) - ->method('getResponse') - ->willReturn($response); - - $event->expects($this->once()) - ->method('getRequest') - ->willReturn($request); - - $subscriber->loginIfRequested($event); - - $records = $handler->getRecords(); - - $this->assertCount(1, $records); - $this->assertSame('loginIfRequested> retry #0 with login not required', $records[0]['message']); - } - - public function testLoginIfRequestedRequested() - { - $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) - ->disableOriginalConstructor() - ->getMock(); - - $authenticator->expects($this->once()) - ->method('isLoginRequired') - ->willReturn(true); - - $authenticator->expects($this->once()) - ->method('login'); - - $builder = new ArraySiteConfigBuilder(['example.com' => [ - 'requiresLogin' => true, - 'notLoggedInXpath' => '//html', - ]]); - $subscriber = new AuthenticatorSubscriber($builder, $authenticator); - - $logger = new Logger('foo'); - $handler = new TestHandler(); - $logger->pushHandler($handler); - - $subscriber->setLogger($logger); - - $response = new Response( - 200, - ['content-type' => 'text/html'], - Stream::factory('') - ); - $guzzle = new Client(); - $guzzle->getEmitter()->attach(new Mock([$response])); - $request = new Request('GET', 'http://www.example.com'); - - $event = $this->getMockBuilder(CompleteEvent::class) - ->disableOriginalConstructor() - ->getMock(); - - $event->expects($this->once()) - ->method('getResponse') - ->willReturn($response); - - $event->expects($this->once()) - ->method('getRequest') - ->willReturn($request); - - $event->expects($this->any()) - ->method('getClient') - ->willReturn($guzzle); - - $subscriber->loginIfRequested($event); - - $records = $handler->getRecords(); - - $this->assertCount(1, $records); - $this->assertSame('loginIfRequested> retry #0 with login required', $records[0]['message']); - } - - public function testLoginIfRequestedRedirect() - { - $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) - ->disableOriginalConstructor() - ->getMock(); - - $builder = new ArraySiteConfigBuilder(['example.com' => [ - 'requiresLogin' => true, - 'notLoggedInXpath' => '//html', - ]]); - $subscriber = new AuthenticatorSubscriber($builder, $authenticator); - - $logger = new Logger('foo'); - $handler = new TestHandler(); - $logger->pushHandler($handler); - - $subscriber->setLogger($logger); - - $response = new Response( - 301, - [], - Stream::factory('') - ); - $guzzle = new Client(); - $guzzle->getEmitter()->attach(new Mock([$response])); - $request = new Request('GET', 'http://www.example.com'); - - $event = $this->getMockBuilder(CompleteEvent::class) - ->disableOriginalConstructor() - ->getMock(); - - $event->expects($this->once()) - ->method('getResponse') - ->willReturn($response); - - $event->expects($this->once()) - ->method('getRequest') - ->willReturn($request); - - $event->expects($this->any()) - ->method('getClient') - ->willReturn($guzzle); - - $subscriber->loginIfRequested($event); - - $records = $handler->getRecords(); - - $this->assertCount(1, $records); - $this->assertSame('loginIfRequested> empty body, ignoring', $records[0]['message']); - } -} diff --git a/tests/SiteConfig/LoginFormAuthenticatorTest.php b/tests/SiteConfig/LoginFormAuthenticatorTest.php index e4f930754..d7e1725ce 100644 --- a/tests/SiteConfig/LoginFormAuthenticatorTest.php +++ b/tests/SiteConfig/LoginFormAuthenticatorTest.php @@ -2,13 +2,12 @@ namespace Tests\Wallabag\SiteConfig; -use GuzzleHttp\Client; -use GuzzleHttp\Message\Response; -use GuzzleHttp\Stream\Stream; -use GuzzleHttp\Subscriber\Mock; use PHPUnit\Framework\TestCase; +use Symfony\Component\BrowserKit\HttpBrowser; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; use Wallabag\ExpressionLanguage\AuthenticatorProvider; use Wallabag\SiteConfig\LoginFormAuthenticator; use Wallabag\SiteConfig\SiteConfig; @@ -17,16 +16,6 @@ class LoginFormAuthenticatorTest extends TestCase { public function testLoginPost() { - $response = new Response( - 200, - ['content-type' => 'text/html'], - Stream::factory('') - ); - $guzzle = new Client(); - $guzzle->getEmitter()->attach(new Mock([$response])); - - $mockHttpClient = new MockHttpClient([new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]); - $siteConfig = new SiteConfig([ 'host' => 'example.com', 'loginUri' => 'http://example.com/login', @@ -40,25 +29,23 @@ class LoginFormAuthenticatorTest extends TestCase 'password' => 'unkn0wn', ]); - $authenticatorProvider = new AuthenticatorProvider($mockHttpClient); - $auth = new LoginFormAuthenticator($authenticatorProvider); - $res = $auth->login($siteConfig, $guzzle); + $browserResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $browserClient = new MockHttpClient([$browserResponse]); + $browser = new HttpBrowser($browserClient); + + $requestHtmlFunctionResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $requestHtmlFunctionClient = new MockHttpClient([$requestHtmlFunctionResponse]); + $authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient); + + $auth = new LoginFormAuthenticator($browser, $authenticatorProvider); + + $res = $auth->login($siteConfig); $this->assertInstanceOf(LoginFormAuthenticator::class, $res); } public function testLoginPostWithExtraFieldsButEmptyHtml() { - $response = new Response( - 200, - ['content-type' => 'text/html'], - Stream::factory('') - ); - $guzzle = new Client(); - $guzzle->getEmitter()->attach(new Mock([$response, $response])); - - $mockHttpClient = new MockHttpClient([new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']])]); - $siteConfig = new SiteConfig([ 'host' => 'example.com', 'loginUri' => 'http://example.com/login', @@ -73,9 +60,17 @@ class LoginFormAuthenticatorTest extends TestCase 'password' => 'unkn0wn', ]); - $authenticatorProvider = new AuthenticatorProvider($mockHttpClient); - $auth = new LoginFormAuthenticator($authenticatorProvider); - $res = $auth->login($siteConfig, $guzzle); + $browserResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $browserClient = new MockHttpClient([$browserResponse]); + $browser = new HttpBrowser($browserClient); + + $requestHtmlFunctionResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $requestHtmlFunctionClient = new MockHttpClient([$requestHtmlFunctionResponse]); + $authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient); + + $auth = new LoginFormAuthenticator($browser, $authenticatorProvider); + + $res = $auth->login($siteConfig); $this->assertInstanceOf(LoginFormAuthenticator::class, $res); } @@ -83,49 +78,6 @@ class LoginFormAuthenticatorTest extends TestCase // testing preg_match public function testLoginPostWithExtraFieldsWithRegex() { - $response = $this->getMockBuilder(Response::class) - ->disableOriginalConstructor() - ->getMock(); - - $response->expects($this->any()) - ->method('getBody') - ->willReturn(file_get_contents(__DIR__ . '/../fixtures/aoc.media.html')); - - $response->expects($this->any()) - ->method('getStatusCode') - ->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) - ->disableOriginalConstructor() - ->getMock(); - - $client->expects($this->any()) - ->method('post') - ->with( - $this->equalTo('https://aoc.media/wp-admin/admin-ajax.php'), - $this->equalTo([ - 'body' => [ - 'nom' => 'johndoe', - 'password' => 'unkn0wn', - 'security' => 'c506c1b8bc', - 'action' => 'login_user', - ], - 'allow_redirects' => true, - 'verify' => false, - ]) - ) - ->willReturn($response); - - $client->expects($this->any()) - ->method('get') - ->with( - $this->equalTo('https://aoc.media/'), - $this->equalTo([]) - ) - ->willReturn($response); - $siteConfig = new SiteConfig([ 'host' => 'aoc.media', 'loginUri' => 'https://aoc.media/wp-admin/admin-ajax.php', @@ -139,78 +91,44 @@ class LoginFormAuthenticatorTest extends TestCase 'password' => 'unkn0wn', ]); - $authenticatorProvider = new AuthenticatorProvider($mockHttpClient); - $auth = new LoginFormAuthenticator($authenticatorProvider); - $res = $auth->login($siteConfig, $client); - - $this->assertInstanceOf(LoginFormAuthenticator::class, $res); - } - - public function testLoginPostWithExtraFieldsWithData() - { - $response = $this->getMockBuilder(Response::class) - ->disableOriginalConstructor() + $browserResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $browserClient = new MockHttpClient([$browserResponse]); + $browser = $this->getMockBuilder(HttpBrowser::class) + ->setConstructorArgs([$browserClient]) ->getMock(); - - $response->expects($this->any()) - ->method('getBody') - ->willReturn(file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html')); - - $response->expects($this->any()) - ->method('getStatusCode') - ->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) - ->disableOriginalConstructor() - ->getMock(); - - $client->expects($this->any()) - ->method('post') + $browser->expects($this->any()) + ->method('request') ->with( - $this->equalTo('https://compte.nextinpact.com/Account/Login'), + $this->equalTo('POST'), + $this->equalTo('https://aoc.media/wp-admin/admin-ajax.php'), $this->equalTo([ - 'body' => [ - 'UserName' => 'johndoe', - 'Password' => 'unkn0wn', - '__RequestVerificationToken' => 's6x2QcnQDUL92mkKSi_JuUBXcgUYx_Plf-KyQ2eJypKAjQZIeTvaFHOsfEdTrcSXt3dt2CW39V7r9V16LUtvjszodAU1', - 'returnUrl' => 'https://www.nextinpact.com/news/102835-pour-cour-comptes-fonctionnement-actuel-vote-par-internet-nest-pas-satisfaisant.htm', - ], - 'allow_redirects' => true, - 'verify' => false, + 'nom' => 'johndoe', + 'password' => 'unkn0wn', + 'security' => 'c506c1b8bc', + 'action' => 'login_user', ]) ) - ->willReturn($response); + ; - $client->expects($this->any()) - ->method('get') + $requestHtmlFunctionResponse = $this->getMockBuilder(ResponseInterface::class)->getMock(); + $requestHtmlFunctionResponse->expects($this->any()) + ->method('getContent') + ->willReturn(file_get_contents(__DIR__ . '/../fixtures/aoc.media.html')) + ; + $requestHtmlFunctionClient = $this->getMockBuilder(HttpClientInterface::class)->getMock(); + $requestHtmlFunctionClient->expects($this->any()) + ->method('request') ->with( - $this->equalTo('https://compte.nextinpact.com/Account/Login?http://www.nextinpact.com/'), - $this->equalTo([ - 'headers' => [ - 'X-Requested-With' => 'XMLHttpRequest', - ], - ]) + $this->equalTo('GET'), + $this->equalTo('https://aoc.media/'), ) - ->willReturn($response); + ->willReturn($requestHtmlFunctionResponse) + ; + $authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient); - $siteConfig = new SiteConfig([ - 'host' => 'nextinpact.com', - 'loginUri' => 'https://compte.nextinpact.com/Account/Login', - 'usernameField' => 'UserName', - 'passwordField' => 'Password', - 'extraFields' => [ - '__RequestVerificationToken' => '@=xpath(\'//form[@action="/Account/Login"]/input[@name="__RequestVerificationToken"]\', request_html(\'https://compte.nextinpact.com/Account/Login?http://www.nextinpact.com/\', {\'headers\': {\'X-Requested-With\':\'XMLHttpRequest\'}}))', - 'returnUrl' => 'https://www.nextinpact.com/news/102835-pour-cour-comptes-fonctionnement-actuel-vote-par-internet-nest-pas-satisfaisant.htm', - ], - 'username' => 'johndoe', - 'password' => 'unkn0wn', - ]); + $auth = new LoginFormAuthenticator($browser, $authenticatorProvider); - $authenticatorProvider = new AuthenticatorProvider($mockHttpClient); - $auth = new LoginFormAuthenticator($authenticatorProvider); - $res = $auth->login($siteConfig, $client); + $res = $auth->login($siteConfig); $this->assertInstanceOf(LoginFormAuthenticator::class, $res); } @@ -225,10 +143,16 @@ class LoginFormAuthenticatorTest extends TestCase 'password' => 'unkn0wn', ]); - $mockHttpClient = new MockHttpClient(); + $browserResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $browserClient = new MockHttpClient([$browserResponse]); + $browser = new HttpBrowser($browserClient); + + $requestHtmlFunctionResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $requestHtmlFunctionClient = new MockHttpClient([$requestHtmlFunctionResponse]); + $authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient); + + $auth = new LoginFormAuthenticator($browser, $authenticatorProvider); - $authenticatorProvider = new AuthenticatorProvider($mockHttpClient); - $auth = new LoginFormAuthenticator($authenticatorProvider); $loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-login.html')); $this->assertFalse($loginRequired); @@ -245,10 +169,16 @@ class LoginFormAuthenticatorTest extends TestCase 'notLoggedInXpath' => '//h2[@class="title_reserve_article"]', ]); - $mockHttpClient = new MockHttpClient(); + $browserResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $browserClient = new MockHttpClient([$browserResponse]); + $browser = new HttpBrowser($browserClient); + + $requestHtmlFunctionResponse = new MockResponse('', ['http_code' => 200, 'response_headers' => ['content-type' => 'text/html']]); + $requestHtmlFunctionClient = new MockHttpClient([$requestHtmlFunctionResponse]); + $authenticatorProvider = new AuthenticatorProvider($requestHtmlFunctionClient); + + $auth = new LoginFormAuthenticator($browser, $authenticatorProvider); - $authenticatorProvider = new AuthenticatorProvider($mockHttpClient); - $auth = new LoginFormAuthenticator($authenticatorProvider); $loginRequired = $auth->isLoginRequired($siteConfig, file_get_contents(__DIR__ . '/../fixtures/nextinpact-article.html')); $this->assertTrue($loginRequired); diff --git a/tests/Tests/Wallabag/HttpClient/AuthenticatorTest.php b/tests/Tests/Wallabag/HttpClient/AuthenticatorTest.php new file mode 100644 index 000000000..e47dc1c68 --- /dev/null +++ b/tests/Tests/Wallabag/HttpClient/AuthenticatorTest.php @@ -0,0 +1,239 @@ +getMockBuilder(LoginFormAuthenticator::class) + ->disableOriginalConstructor() + ->getMock(); + + $builder = new ArraySiteConfigBuilder(['example.com' => []]); + $subscriber = new Authenticator($builder, $authenticator); + + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); + + $subscriber->setLogger($logger); + + $login = $subscriber->loginIfRequired('http://www.example.com'); + + $this->assertFalse($login); + + $records = $handler->getRecords(); + + $this->assertCount(1, $records); + $this->assertSame('loginIfRequired> will not require login', $records[0]['message']); + } + + public function testLoginIfRequiredWithNotLoggedInUser() + { + $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) + ->disableOriginalConstructor() + ->getMock(); + + $authenticator->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(false); + + $authenticator->expects($this->once()) + ->method('login'); + + $builder = new ArraySiteConfigBuilder(['example.com' => ['requiresLogin' => true]]); + $subscriber = new Authenticator($builder, $authenticator); + + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); + + $subscriber->setLogger($logger); + + $login = $subscriber->loginIfRequired('http://www.example.com'); + + $this->assertTrue($login); + + $records = $handler->getRecords(); + + $this->assertCount(1, $records); + $this->assertSame('loginIfRequired> user is not logged in, attach authenticator', $records[0]['message']); + } + + public function testLoginIfRequestedNotRequired() + { + $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) + ->disableOriginalConstructor() + ->getMock(); + + $builder = new ArraySiteConfigBuilder(['example.com' => []]); + $subscriber = new Authenticator($builder, $authenticator); + + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); + + $subscriber->setLogger($logger); + + $response = $this->getMockBuilder(ResponseInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $response->expects($this->once()) + ->method('getInfo') + ->with($this->equalTo('url')) + ->willReturn('http://www.example.com'); + + $login = $subscriber->loginIfRequested($response); + + $this->assertFalse($login); + + $records = $handler->getRecords(); + + $this->assertCount(1, $records); + $this->assertSame('loginIfRequested> will not require login', $records[0]['message']); + } + + public function testLoginIfRequestedNotRequested() + { + $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) + ->disableOriginalConstructor() + ->getMock(); + + $authenticator->expects($this->once()) + ->method('isLoginRequired') + ->willReturn(false); + + $builder = new ArraySiteConfigBuilder(['example.com' => [ + 'requiresLogin' => true, + 'notLoggedInXpath' => '//html', + ]]); + $subscriber = new Authenticator($builder, $authenticator); + + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); + + $subscriber->setLogger($logger); + + $response = $this->getMockBuilder(ResponseInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $response->expects($this->once()) + ->method('getInfo') + ->with($this->equalTo('url')) + ->willReturn('http://www.example.com'); + + $response->expects($this->once()) + ->method('getContent') + ->willReturn(''); + + $login = $subscriber->loginIfRequested($response); + + $this->assertFalse($login); + + $records = $handler->getRecords(); + + $this->assertCount(1, $records); + $this->assertSame('loginIfRequested> retry with login not required', $records[0]['message']); + } + + public function testLoginIfRequestedRequested() + { + $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) + ->disableOriginalConstructor() + ->getMock(); + + $authenticator->expects($this->once()) + ->method('isLoginRequired') + ->willReturn(true); + + $authenticator->expects($this->once()) + ->method('login'); + + $builder = new ArraySiteConfigBuilder(['example.com' => [ + 'requiresLogin' => true, + 'notLoggedInXpath' => '//html', + ]]); + $subscriber = new Authenticator($builder, $authenticator); + + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); + + $subscriber->setLogger($logger); + + $response = $this->getMockBuilder(ResponseInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $response->expects($this->once()) + ->method('getInfo') + ->with($this->equalTo('url')) + ->willReturn('http://www.example.com'); + + $response->expects($this->once()) + ->method('getContent') + ->willReturn(''); + + $login = $subscriber->loginIfRequested($response); + + $this->assertTrue($login); + + $records = $handler->getRecords(); + + $this->assertCount(1, $records); + $this->assertSame('loginIfRequested> retry with login required', $records[0]['message']); + } + + public function testLoginIfRequestedRedirect() + { + $authenticator = $this->getMockBuilder(LoginFormAuthenticator::class) + ->disableOriginalConstructor() + ->getMock(); + + $builder = new ArraySiteConfigBuilder(['example.com' => [ + 'requiresLogin' => true, + 'notLoggedInXpath' => '//html', + ]]); + $subscriber = new Authenticator($builder, $authenticator); + + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); + + $subscriber->setLogger($logger); + + $response = $this->getMockBuilder(ResponseInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $response->expects($this->once()) + ->method('getInfo') + ->with($this->equalTo('url')) + ->willReturn('http://www.example.com'); + + $response->expects($this->once()) + ->method('getContent') + ->willReturn(''); + + $login = $subscriber->loginIfRequested($response); + + $this->assertFalse($login); + + $records = $handler->getRecords(); + + $this->assertCount(1, $records); + $this->assertSame('loginIfRequested> empty body, ignoring', $records[0]['message']); + } +}