From: Bertrand Dunogier Date: Thu, 29 Sep 2016 08:14:43 +0000 (+0200) Subject: Added authentication for restricted access articles X-Git-Tag: 2.2.0~3^2~49^2~3 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=7aab0ecf2f78ce58f28b53c1fa19bfd824cc3cd7;p=github%2Fwallabag%2Fwallabag.git Added authentication for restricted access articles Fix #438. Thank you so much @bdunogier --- diff --git a/app/AppKernel.php b/app/AppKernel.php index 81b83ef9..c8382d5f 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -31,6 +31,7 @@ class AppKernel extends Kernel new Craue\ConfigBundle\CraueConfigBundle(), new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(), new FOS\JsRoutingBundle\FOSJsRoutingBundle(), + new BD\GuzzleSiteAuthenticatorBundle\BDGuzzleSiteAuthenticatorBundle(), // wallabag bundles new Wallabag\CoreBundle\WallabagCoreBundle(), diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist index f821f2a8..a4dc0bde 100644 --- a/app/config/parameters.yml.dist +++ b/app/config/parameters.yml.dist @@ -56,3 +56,6 @@ parameters: redis_port: 6379 redis_path: null redis_password: null + + # sites credentials + sites_credentials: {} diff --git a/composer.json b/composer.json index 1548d6ec..e2aeb424 100644 --- a/composer.json +++ b/composer.json @@ -83,7 +83,8 @@ "predis/predis": "^1.0", "javibravo/simpleue": "^1.0", "symfony/dom-crawler": "^3.1", - "friendsofsymfony/jsrouting-bundle": "^1.6" + "friendsofsymfony/jsrouting-bundle": "^1.6", + "bdunogier/guzzle-site-authenticator": "^1.0@beta" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2", diff --git a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php new file mode 100644 index 00000000..6d4129e8 --- /dev/null +++ b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php @@ -0,0 +1,68 @@ +grabyConfigBuilder = $grabyConfigBuilder; + $this->credentials = $credentials; + } + + /** + * Builds the SiteConfig for a host. + * + * @param string $host The "www." prefix is ignored + * + * @return SiteConfig + * + * @throws OutOfRangeException If there is no config for $host + */ + public function buildForHost($host) + { + // required by credentials below + $host = strtolower($host); + if (substr($host, 0, 4) == 'www.') { + $host = substr($host, 4); + } + + $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' => is_array($config->login_extra_fields) ? $config->login_extra_fields : [], + 'notLoggedInXpath' => $config->not_logged_in_xpath ?: null, + ]; + + if (isset($this->credentials[$host])) { + $parameters['username'] = $this->credentials[$host]['username']; + $parameters['password'] = $this->credentials[$host]['password']; + } + + return new SiteConfig($parameters); + } +} diff --git a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php new file mode 100644 index 00000000..3e1d1ed5 --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php @@ -0,0 +1,44 @@ +authenticatorSubscriber = $authenticatorSubscriber; + $this->cookieJar = $cookieJar; + } + + /** + * @return \GuzzleHttp\Client + */ + public function buildHttpClient() + { + // need to set the (shared) cookie jar + $client = new Client(['handler' => new SafeCurlHandler(), 'defaults' => ['cookies' => $this->cookieJar]]); + $client->getEmitter()->attach($this->authenticatorSubscriber); + + return $client; + } +} diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 0280bc18..1b7f39fc 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml @@ -41,11 +41,43 @@ services: arguments: - error_message: '%wallabag_core.fetching_error_message%' + - "@wallabag_core.guzzle.http_client" + - "@wallabag_core.graby.config_builder" calls: - [ setLogger, [ "@logger" ] ] tags: - { name: monolog.logger, channel: graby } + wallabag_core.graby.config_builder: + class: Graby\SiteConfig\ConfigBuilder + arguments: + - {} + - "@logger" + + wallabag_core.guzzle.http_client: + class: GuzzleHttp\ClientInterface + factory: ["@wallabag_core.guzzle.http_client_factory", buildHttpClient] + + wallabag_core.guzzle_authenticator.config_builder: + class: Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder + arguments: + - "@wallabag_core.graby.config_builder" + - "%sites_credentials%" + + # service alias override + bd_guzzle_site_authenticator.site_config_builder: + alias: wallabag_core.guzzle_authenticator.config_builder + + wallabag_core.guzzle.http_client_factory: + class: Wallabag\CoreBundle\Helper\HttpClientFactory + arguments: + - "@bd_guzzle_site_authenticator.authenticator_subscriber" + - "@wallabag_core.guzzle.cookie_jar" + + wallabag_core.guzzle.cookie_jar: + class: GuzzleHttp\Cookie\FileCookieJar + arguments: ["%kernel.cache_dir%/cookiejar.json"] + wallabag_core.content_proxy: class: Wallabag\CoreBundle\Helper\ContentProxy arguments: diff --git a/tests/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilderTest.php b/tests/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilderTest.php new file mode 100644 index 00000000..aee67259 --- /dev/null +++ b/tests/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilderTest.php @@ -0,0 +1,85 @@ +getMockBuilder('\Graby\SiteConfig\ConfigBuilder') + ->disableOriginalConstructor() + ->getMock(); + + $grabySiteConfig = new GrabySiteConfig(); + $grabySiteConfig->requires_login = true; + $grabySiteConfig->login_uri = 'http://example.com/login'; + $grabySiteConfig->login_username_field = 'login'; + $grabySiteConfig->login_password_field = 'password'; + $grabySiteConfig->login_extra_fields = ['field' => 'value']; + $grabySiteConfig->not_logged_in_xpath = '//div[@class="need-login"]'; + + $grabyConfigBuilderMock + ->method('buildForHost') + ->with('example.com') + ->will($this->returnValue($grabySiteConfig)); + + $this->builder = new GrabySiteConfigBuilder( + $grabyConfigBuilderMock, + ['example.com' => ['username' => 'foo', 'password' => 'bar']] + ); + + $config = $this->builder->buildForHost('example.com'); + + self::assertEquals( + new SiteConfig([ + 'host' => 'example.com', + 'requiresLogin' => true, + 'loginUri' => 'http://example.com/login', + 'usernameField' => 'login', + 'passwordField' => 'password', + 'extraFields' => ['field' => 'value'], + 'notLoggedInXpath' => '//div[@class="need-login"]', + 'username' => 'foo', + 'password' => 'bar', + ]), + $config + ); + } + + public function testBuildConfigDoesntExist() + { + /* @var \Graby\SiteConfig\ConfigBuilder|\PHPUnit_Framework_MockObject_MockObject */ + $grabyConfigBuilderMock = $this->getMockBuilder('\Graby\SiteConfig\ConfigBuilder') + ->disableOriginalConstructor() + ->getMock(); + + $grabyConfigBuilderMock + ->method('buildForHost') + ->with('unknown.com') + ->will($this->returnValue(new GrabySiteConfig())); + + $this->builder = new GrabySiteConfigBuilder($grabyConfigBuilderMock, []); + + $config = $this->builder->buildForHost('unknown.com'); + + self::assertEquals( + new SiteConfig([ + 'host' => 'unknown.com', + 'requiresLogin' => false, + 'username' => null, + 'password' => null, + 'extraFields' => [], + ]), + $config + ); + } +}