From 820f9b9ae42a58226600306739bb8186141feda7 Mon Sep 17 00:00:00 2001 From: Joseph Bielawski Date: Fri, 1 Jan 2021 11:51:11 +0100 Subject: [PATCH] Rework resource owners to use Symfony Http Client internally --- CHANGELOG.md | 2 + DependencyInjection/Configuration.php | 16 - DependencyInjection/HWIOAuthExtension.php | 34 +- OAuth/ResourceOwner/AbstractResourceOwner.php | 61 ++-- OAuth/ResourceOwner/AppleResourceOwner.php | 16 +- OAuth/ResourceOwner/DropboxResourceOwner.php | 25 +- OAuth/ResourceOwner/FiwareResourceOwner.php | 3 +- .../ResourceOwner/FoursquareResourceOwner.php | 6 +- .../GenericOAuth1ResourceOwner.php | 4 +- .../GenericOAuth2ResourceOwner.php | 21 +- OAuth/ResourceOwner/JiraResourceOwner.php | 20 +- OAuth/ResourceOwner/MailRuResourceOwner.php | 26 +- .../OdnoklassnikiResourceOwner.php | 20 +- OAuth/ResourceOwner/QQResourceOwner.php | 16 +- .../SensioConnectResourceOwner.php | 29 ++ .../ResourceOwner/SinaWeiboResourceOwner.php | 20 +- OAuth/ResourceOwner/SpotifyResourceOwner.php | 20 +- .../StackExchangeResourceOwner.php | 26 +- OAuth/ResourceOwner/TraktResourceOwner.php | 18 +- .../ResourceOwner/VkontakteResourceOwner.php | 31 +- Resources/config/http_client.xml | 13 - Resources/config/oauth.xml | 4 +- Resources/doc/1-setting_up_the_bundle.md | 14 +- Resources/doc/index.md | 1 - .../internals/configuring_the_http_client.md | 121 ------- Resources/doc/resource_owners/fiware.md | 9 - Tests/App/config.yml | 7 +- .../HWIOAuthExtensionTest.php | 37 +-- .../Controller/ConnectControllerTest.php | 47 +-- .../Controller/LoginControllerTest.php | 12 +- Tests/Functional/IntegrationTest.php | 29 +- .../ResourceOwner/AppleResourceOwnerTest.php | 87 ++--- .../ResourceOwner/Auth0ResourceOwnerTest.php | 134 +------- .../ResourceOwner/AzureResourceOwnerTest.php | 55 ++-- .../BitbucketResourceOwnerTest.php | 10 +- .../ResourceOwner/BoxResourceOwnerTest.php | 26 +- .../BufferAppResourceOwnerTest.php | 29 +- .../ResourceOwner/CleverResourceOwnerTest.php | 8 - .../DailymotionResourceOwnerTest.php | 4 +- .../ResourceOwner/DeezerResourceOwnerTest.php | 10 +- .../DeviantartResourceOwnerTest.php | 13 +- .../DropboxResourceOwnerTest.php | 4 +- .../EventbriteResourceOwnerTest.php | 10 +- .../FacebookResourceOwnerTest.php | 49 ++- .../ResourceOwner/FlickrResourceOwnerTest.php | 24 +- .../FoursquareResourceOwnerTest.php | 10 +- .../GenericOAuth1ResourceOwnerTest.php | 219 ++++++++----- .../GenericOAuth2ResourceOwnerTest.php | 307 ++++++++++-------- .../ResourceOwner/GitHubResourceOwnerTest.php | 75 ++++- .../ResourceOwner/GitLabResourceOwnerTest.php | 11 +- .../ResourceOwner/GoogleResourceOwnerTest.php | 50 ++- .../ResourceOwner/HubicResourceOwnerTest.php | 14 +- .../InstagramResourceOwnerTest.php | 8 +- .../ItembaseResourceOwnerTest.php | 2 - .../ResourceOwner/JiraResourceOwnerTest.php | 71 ++-- .../LinkedinResourceOwnerTest.php | 37 ++- .../OdnoklassnikiResourceOwnerTest.php | 6 +- .../ResourceOwner/PaypalResourceOwnerTest.php | 2 - .../ResourceOwner/QQResourceOwnerTest.php | 70 +++- .../ResourceOwner/ResourceOwnerTestCase.php | 75 ++--- .../RunKeeperResourceOwnerTest.php | 14 +- .../SalesforceResourceOwnerTest.php | 38 ++- .../SensioConnectResourceOwnerTest.php | 30 +- .../SinaWeiboResourceOwnerTest.php | 45 ++- .../SpotifyResourceOwnerTest.php | 23 +- .../StackExchangeResourceOwnerTest.php | 6 +- .../StereomoodResourceOwnerTest.php | 11 +- .../ResourceOwner/StravaResourceOwnerTest.php | 12 +- .../ResourceOwner/ToshlResourceOwnerTest.php | 35 +- .../ResourceOwner/TraktResourceOwnerTest.php | 10 +- .../ResourceOwner/TrelloResourceOwnerTest.php | 12 +- .../TwitterResourceOwnerTest.php | 19 +- .../ResourceOwner/XingResourceOwnerTest.php | 10 +- .../ResourceOwner/YahooResourceOwnerTest.php | 20 +- .../YoutubeResourceOwnerTest.php | 48 ++- composer.json | 11 +- 76 files changed, 1336 insertions(+), 1136 deletions(-) delete mode 100644 Resources/config/http_client.xml delete mode 100644 Resources/doc/internals/configuring_the_http_client.md diff --git a/CHANGELOG.md b/CHANGELOG.md index dcbce393f..85a6ab7f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Changelog * BC Break: removed configuration option `fosub` from `oauth_user_provider`, * BC Break: removed configuration options `hwi_oauth.fosub`, & all related DI parameters, * BC Break: removed DI parameter `hwi_oauth.registration.form.factory` in favour of declaring form class name as DI parameter: `hwi_oauth.connect.registration_form`, +* BC Break: replaced `php-http/httplug-bundle` with `symfony/http-client` +* BC Break: removed `hwi_oauth.http` configuration ## 1.4.1 (2021-07-28) * Bugfix: Define missing `hwi_oauth.connect.confirmation` parameter, diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index a3b537750..e3185a8e9 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -180,7 +180,6 @@ public function getConfigTreeBuilder() ->end() ; - $this->addHttpClientConfiguration($rootNode); $this->addConnectConfiguration($rootNode); $this->addResourceOwnersConfiguration($rootNode); @@ -431,21 +430,6 @@ private function addResourceOwnersConfiguration(ArrayNodeDefinition $node): void ; } - private function addHttpClientConfiguration(ArrayNodeDefinition $node): void - { - $node - ->children() - ->arrayNode('http') - ->addDefaultsIfNotSet() - ->children() - ->scalarNode('client')->defaultValue('httplug.client.default')->end() - ->scalarNode('message_factory')->defaultValue('httplug.message_factory.default')->end() - ->end() - ->end() - ->end() - ; - } - private function addConnectConfiguration(ArrayNodeDefinition $node): void { $node diff --git a/DependencyInjection/HWIOAuthExtension.php b/DependencyInjection/HWIOAuthExtension.php index b39539eb7..ce62b57c4 100644 --- a/DependencyInjection/HWIOAuthExtension.php +++ b/DependencyInjection/HWIOAuthExtension.php @@ -20,9 +20,12 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Contracts\HttpClient\HttpClientInterface; /** * @author Geoffrey Bachelet @@ -39,14 +42,13 @@ final class HWIOAuthExtension extends Extension * @throws InvalidConfigurationException * @throws BadMethodCallException * @throws InvalidArgumentException - * @throws \Symfony\Component\DependencyInjection\Exception\OutOfBoundsException - * @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + * @throws OutOfBoundsException + * @throws ServiceNotFoundException */ public function load(array $configs, ContainerBuilder $container) { $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config/')); $loader->load('controller.xml'); - $loader->load('http_client.xml'); $loader->load('oauth.xml'); $loader->load('templating.xml'); $loader->load('twig.xml'); @@ -55,8 +57,6 @@ public function load(array $configs, ContainerBuilder $container) $processor = new Processor(); $config = $processor->processConfiguration(new Configuration(), $configs); - $this->createHttplugClient($container, $config); - // set current firewall if (empty($config['firewall_names'])) { throw new InvalidConfigurationException('The child node "firewall_names" at path "hwi_oauth" must be configured.'); @@ -110,7 +110,7 @@ public function load(array $configs, ContainerBuilder $container) * @throws BadMethodCallException * @throws InvalidArgumentException */ - public function createResourceOwnerService(ContainerBuilder $container, $name, array $options) + public function createResourceOwnerService(ContainerBuilder $container, string $name, array $options): void { // alias services if (isset($options['service'])) { @@ -137,8 +137,9 @@ public function createResourceOwnerService(ContainerBuilder $container, $name, a $definition->setClass("%hwi_oauth.resource_owner.$type.class%"); } - $definition->replaceArgument(2, $options); - $definition->replaceArgument(3, $name); + $definition->replaceArgument(1, $options); + $definition->replaceArgument(2, $name); + $definition->replaceArgument('$httpClient', new Reference(HttpClientInterface::class)); $definition->setPublic(true); $container->setDefinition('hwi_oauth.resource_owner.'.$name, $definition); @@ -152,23 +153,6 @@ public function getAlias() return 'hwi_oauth'; } - protected function createHttplugClient(ContainerBuilder $container, array $config) - { - $httpClientId = $config['http']['client']; - $httpMessageFactoryId = $config['http']['message_factory']; - $bundles = $container->getParameter('kernel.bundles'); - - if ('httplug.client.default' === $httpClientId && !isset($bundles['HttplugBundle'])) { - throw new InvalidConfigurationException('You must setup php-http/httplug-bundle to use the default http client service.'); - } - if ('httplug.message_factory.default' === $httpMessageFactoryId && !isset($bundles['HttplugBundle'])) { - throw new InvalidConfigurationException('You must setup php-http/httplug-bundle to use the default http message factory service.'); - } - - $container->setAlias('hwi_oauth.http.client', new Alias($config['http']['client'], true)); - $container->setAlias('hwi_oauth.http.message_factory', new Alias($config['http']['message_factory'], true)); - } - /** * Check of the connect controllers etc should be enabled. * diff --git a/OAuth/ResourceOwner/AbstractResourceOwner.php b/OAuth/ResourceOwner/AbstractResourceOwner.php index fb4400c55..a01abc5c7 100644 --- a/OAuth/ResourceOwner/AbstractResourceOwner.php +++ b/OAuth/ResourceOwner/AbstractResourceOwner.php @@ -11,8 +11,6 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; -use Http\Client\Common\HttpMethodsClient; -use Http\Client\Exception; use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\OAuth\RequestDataStorageInterface; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; @@ -20,11 +18,14 @@ use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface; use HWI\Bundle\OAuthBundle\OAuth\State\State; use HWI\Bundle\OAuthBundle\OAuth\StateInterface; -use Psr\Http\Message\ResponseInterface; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\HttpFoundation\Request as HttpRequest; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * AbstractResourceOwner. @@ -47,7 +48,7 @@ abstract class AbstractResourceOwner implements ResourceOwnerInterface protected $paths = []; /** - * @var HttpMethodsClient + * @var HttpClientInterface */ protected $httpClient; @@ -77,18 +78,15 @@ abstract class AbstractResourceOwner implements ResourceOwnerInterface private $stateLoaded = false; /** - * @param HttpMethodsClient $httpClient Httplug client - * @param HttpUtils $httpUtils Http utils - * @param array $options Options for the resource owner - * @param string $name Name for the resource owner - * @param RequestDataStorageInterface $storage Request token storage + * @param array $options Options for the resource owner + * @param string $name Name for the resource owner */ public function __construct( - HttpMethodsClient $httpClient, HttpUtils $httpUtils, array $options, string $name, - RequestDataStorageInterface $storage + RequestDataStorageInterface $storage, + HttpClientInterface $httpClient ) { $this->httpClient = $httpClient; $this->httpUtils = $httpUtils; @@ -303,46 +301,41 @@ protected function httpRequest($url, $content = null, array $headers = [], $meth $method = null === $content || '' === $content ? 'GET' : 'POST'; } - $headers += ['User-Agent' => 'HWIOAuthBundle (https://github.com/hwi/HWIOAuthBundle)']; + $options = ['headers' => $headers]; + $options['headers'] += ['User-Agent' => 'HWIOAuthBundle (https://github.com/hwi/HWIOAuthBundle)']; if (\is_string($content)) { - if (!isset($headers['Content-Length'])) { - $headers += ['Content-Length' => (string) \strlen($content)]; + if (!isset($options['headers']['Content-Length'])) { + $options['headers'] += ['Content-Length' => (string) \strlen($content)]; } } elseif (\is_array($content)) { - $content = http_build_query($content, '', '&'); + $options['body'] = $content; } try { - return $this->httpClient->send( + return $this->httpClient->request( $method, $url, - $headers, - $content + $options ); - } catch (Exception $e) { + } catch (TransportExceptionInterface $e) { throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); } } - /** - * Get the 'parsed' content based on the response headers. - * - * @return array - */ - protected function getResponseContent(ResponseInterface $rawResponse) + protected function getResponseContent(ResponseInterface $rawResponse): array { - // First check that content in response exists, due too bug: https://bugs.php.net/bug.php?id=54484 - $content = (string) $rawResponse->getBody(); - if (!$content) { - return []; - } + $contentTypes = $rawResponse->getHeaders(false)['content-type'] ?? []; + if (\in_array('text/plain', $contentTypes, true)) { + parse_str($rawResponse->getContent(false), $response); - $response = json_decode($content, true); - if (\JSON_ERROR_NONE !== json_last_error()) { - parse_str($content, $response); + return $response; } - return $response; + try { + return $rawResponse->toArray(false); + } catch (JsonException $e) { + return []; + } } /** diff --git a/OAuth/ResourceOwner/AppleResourceOwner.php b/OAuth/ResourceOwner/AppleResourceOwner.php index ecea85a14..31268b40a 100644 --- a/OAuth/ResourceOwner/AppleResourceOwner.php +++ b/OAuth/ResourceOwner/AppleResourceOwner.php @@ -52,10 +52,10 @@ public function getAuthorizationUrl($redirectUri, array $extraParameters = []) public function getUserInformation(array $accessToken, array $extraParameters = []) { if (!isset($accessToken['id_token'])) { - throw new \Exception('Undefined index id_token'); + throw new \InvalidArgumentException('Undefined index id_token'); } - $jwt = self::jwt_decode($accessToken['id_token']); + $jwt = self::jwtDecode($accessToken['id_token']); $data = json_decode(base64_decode($jwt), true); if (isset($accessToken['firstName'], $accessToken['lastName'])) { @@ -105,9 +105,10 @@ public function getAccessToken(Request $request, $redirectUri, array $extraParam */ public function refreshAccessToken($refreshToken, array $extraParameters = []) { - $parameters = []; - $parameters['client_id'] = $this->options['client_id']; - $parameters['client_secret'] = $this->options['client_secret']; + $parameters = [ + 'client_id' => $this->options['client_id'], + 'client_secret' => $this->options['client_secret'], + ]; return parent::refreshAccessToken($refreshToken, array_merge($parameters, $extraParameters)); } @@ -130,7 +131,6 @@ protected function configureOptions(OptionsResolver $resolver) $resolver->setDefaults([ 'authorization_url' => 'https://appleid.apple.com/auth/authorize', 'access_token_url' => 'https://appleid.apple.com/auth/token', - 'revoke_token_url' => '', 'infos_url' => '', 'use_commas_in_scope' => false, 'display' => null, @@ -140,10 +140,10 @@ protected function configureOptions(OptionsResolver $resolver) ]); } - private static function jwt_decode($id_token) + private static function jwtDecode(string $idToken) { //// from http://stackoverflow.com/a/28748285/624544 - [, $jwt] = explode('.', $id_token, 3); + [, $jwt] = explode('.', $idToken, 3); // if the token was urlencoded, do some fixes to ensure that it is valid base64 encoded $jwt = str_replace(['-', '_'], ['+', '/'], $jwt); diff --git a/OAuth/ResourceOwner/DropboxResourceOwner.php b/OAuth/ResourceOwner/DropboxResourceOwner.php index 5cb3a2239..a58014b95 100644 --- a/OAuth/ResourceOwner/DropboxResourceOwner.php +++ b/OAuth/ResourceOwner/DropboxResourceOwner.php @@ -11,10 +11,13 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; -use Psr\Http\Message\ResponseInterface; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * DropboxResourceOwner. @@ -45,8 +48,7 @@ public function getUserInformation(array $accessToken, ) { if ($this->options['use_bearer_authorization']) { $content = $this->httpRequest( - $this->normalizeUrl($this->options['infos_url'], - $extraParameters), + $this->normalizeUrl($this->options['infos_url'], $extraParameters), 'null', [ 'Authorization' => 'Bearer'.' '.$accessToken['access_token'], @@ -57,18 +59,21 @@ public function getUserInformation(array $accessToken, $content = $this->doGetUserInformationRequest( $this->normalizeUrl( $this->options['infos_url'], - array_merge([$this->options['attr_name'] => $accessToken['access_token']], - $extraParameters) + array_merge([$this->options['attr_name'] => $accessToken['access_token']], $extraParameters) ) ); } - $response = $this->getUserResponse(); - $response->setData($content instanceof ResponseInterface ? (string) $content->getBody() : $content); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + try { + $response = $this->getUserResponse(); + $response->setData($content instanceof ResponseInterface ? $content->toArray(false) : $content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - return $response; + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/FiwareResourceOwner.php b/OAuth/ResourceOwner/FiwareResourceOwner.php index 4f4c1f01c..64602584f 100644 --- a/OAuth/ResourceOwner/FiwareResourceOwner.php +++ b/OAuth/ResourceOwner/FiwareResourceOwner.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * FiwareResourceOwner. @@ -86,7 +87,7 @@ public function getUserInformation(array $accessToken, array $extraParameters = } $response = $this->getUserResponse(); - $response->setData((string) $content->getBody()); + $response->setData($content instanceof ResponseInterface ? $content->getContent() : $content); $response->setResourceOwner($this); $response->setOAuthToken(new OAuthToken($accessToken)); diff --git a/OAuth/ResourceOwner/FoursquareResourceOwner.php b/OAuth/ResourceOwner/FoursquareResourceOwner.php index c47e38dbb..df669b30b 100644 --- a/OAuth/ResourceOwner/FoursquareResourceOwner.php +++ b/OAuth/ResourceOwner/FoursquareResourceOwner.php @@ -11,8 +11,8 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; -use Psr\Http\Message\ResponseInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * FoursquareResourceOwner. @@ -39,14 +39,14 @@ class FoursquareResourceOwner extends GenericOAuth2ResourceOwner /** * {@inheritdoc} */ - protected function getResponseContent(ResponseInterface $rawResponse) + protected function getResponseContent(ResponseInterface $rawResponse): array { $response = parent::getResponseContent($rawResponse); // Foursquare use quite custom response structure in case of error if (isset($response['meta']['errorType'])) { // Prevent to mark deprecated calls as errors - if (200 == $response['meta']['code']) { + if (200 === (int) $response['meta']['code']) { $response['error'] = $response['meta']['errorType']; // Try to add some details of error if available if (isset($response['meta']['errorMessage'])) { diff --git a/OAuth/ResourceOwner/GenericOAuth1ResourceOwner.php b/OAuth/ResourceOwner/GenericOAuth1ResourceOwner.php index 571be13d9..887b79851 100644 --- a/OAuth/ResourceOwner/GenericOAuth1ResourceOwner.php +++ b/OAuth/ResourceOwner/GenericOAuth1ResourceOwner.php @@ -15,10 +15,10 @@ use HWI\Bundle\OAuthBundle\Security\Helper\NonceGenerator; use HWI\Bundle\OAuthBundle\Security\OAuthErrorHandler; use HWI\Bundle\OAuthBundle\Security\OAuthUtils; -use Psr\Http\Message\ResponseInterface; use Symfony\Component\HttpFoundation\Request as HttpRequest; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * GenericOAuth1ResourceOwner. @@ -54,7 +54,7 @@ public function getUserInformation(array $accessToken, array $extraParameters = $content = $this->doGetUserInformationRequest($url, $parameters); $response = $this->getUserResponse(); - $response->setData($content instanceof ResponseInterface ? (string) $content->getBody() : $content); + $response->setData($content instanceof ResponseInterface ? $content->getContent() : $content); $response->setResourceOwner($this); $response->setOAuthToken(new OAuthToken($accessToken)); diff --git a/OAuth/ResourceOwner/GenericOAuth2ResourceOwner.php b/OAuth/ResourceOwner/GenericOAuth2ResourceOwner.php index eced2d400..af60a35bd 100644 --- a/OAuth/ResourceOwner/GenericOAuth2ResourceOwner.php +++ b/OAuth/ResourceOwner/GenericOAuth2ResourceOwner.php @@ -11,14 +11,17 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; use HWI\Bundle\OAuthBundle\Security\Helper\NonceGenerator; use HWI\Bundle\OAuthBundle\Security\OAuthErrorHandler; -use Psr\Http\Message\ResponseInterface; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\HttpFoundation\Request as HttpRequest; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Geoffrey Bachelet @@ -46,12 +49,16 @@ public function getUserInformation(array $accessToken, array $extraParameters = ); } - $response = $this->getUserResponse(); - $response->setData($content instanceof ResponseInterface ? (string) $content->getBody() : $content); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); - - return $response; + try { + $response = $this->getUserResponse(); + $response->setData($content instanceof ResponseInterface ? $content->toArray(false) : $content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); + + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/JiraResourceOwner.php b/OAuth/ResourceOwner/JiraResourceOwner.php index 5482743e7..623f6f81c 100644 --- a/OAuth/ResourceOwner/JiraResourceOwner.php +++ b/OAuth/ResourceOwner/JiraResourceOwner.php @@ -11,12 +11,14 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; use HWI\Bundle\OAuthBundle\Security\Helper\NonceGenerator; use HWI\Bundle\OAuthBundle\Security\OAuthUtils; -use Psr\Http\Message\ResponseInterface; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * JiraResourceOwner. @@ -75,14 +77,18 @@ public function getUserInformation(array $accessToken, array $extraParameters = $this->options['signature_method'] ); - $content = $this->doGetUserInformationRequest($url, $parameters); + try { + $content = $this->doGetUserInformationRequest($url, $parameters); - $response = $this->getUserResponse(); - $response->setData($content instanceof ResponseInterface ? (string) $content->getBody() : $content); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + $response = $this->getUserResponse(); + $response->setData($content instanceof ResponseInterface ? $content->getContent(false) : $content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - return $response; + return $response; + } catch (TransportExceptionInterface $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/MailRuResourceOwner.php b/OAuth/ResourceOwner/MailRuResourceOwner.php index 878df10d9..dca5d417d 100644 --- a/OAuth/ResourceOwner/MailRuResourceOwner.php +++ b/OAuth/ResourceOwner/MailRuResourceOwner.php @@ -11,8 +11,11 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; /** * MailRuResourceOwner. @@ -49,18 +52,21 @@ public function getUserInformation(array $accessToken, array $extraParameters = $url = $this->normalizeUrl($this->options['infos_url'], $params); - $content = (string) $this->doGetUserInformationRequest($url)->getBody(); - $content = json_decode($content, true); - if (isset($content[0])) { - $content = (array) $content[0]; - } + try { + $content = $this->doGetUserInformationRequest($url)->toArray(false); + if (isset($content[0])) { + $content = (array) $content[0]; + } - $response = $this->getUserResponse(); - $response->setData($content); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + $response = $this->getUserResponse(); + $response->setData($content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - return $response; + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/OdnoklassnikiResourceOwner.php b/OAuth/ResourceOwner/OdnoklassnikiResourceOwner.php index 9ebf9196c..e76ac2c8f 100644 --- a/OAuth/ResourceOwner/OdnoklassnikiResourceOwner.php +++ b/OAuth/ResourceOwner/OdnoklassnikiResourceOwner.php @@ -11,9 +11,12 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; /** * OdnoklassnikiResourceOwner. @@ -61,16 +64,21 @@ public function getUserInformation(array $accessToken, array $extraParameters = md5($accessToken['access_token'].$this->options['client_secret']) )); } + $url = $this->normalizeUrl($this->options['infos_url'], $parameters); - $content = $this->doGetUserInformationRequest($url)->getBody(); + try { + $content = $this->doGetUserInformationRequest($url)->toArray(false); - $response = $this->getUserResponse(); - $response->setData((string) $content); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + $response = $this->getUserResponse(); + $response->setData($content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - return $response; + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/QQResourceOwner.php b/OAuth/ResourceOwner/QQResourceOwner.php index 120a28c8f..d9b059e9b 100644 --- a/OAuth/ResourceOwner/QQResourceOwner.php +++ b/OAuth/ResourceOwner/QQResourceOwner.php @@ -11,11 +11,10 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; -use Http\Discovery\MessageFactoryDiscovery; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; -use Psr\Http\Message\ResponseInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @final since 1.4 @@ -35,18 +34,11 @@ class QQResourceOwner extends GenericOAuth2ResourceOwner /** * {@inheritdoc} */ - public function getResponseContent(ResponseInterface $rawResponse) + public function getResponseContent(ResponseInterface $rawResponse): array { - $content = (string) $rawResponse->getBody(); + $content = $rawResponse->getContent(false); if (preg_match('/^callback\((.+)\);$/', $content, $matches)) { - $rawResponse = MessageFactoryDiscovery::find() - ->createResponse( - $rawResponse->getStatusCode(), - null, - $rawResponse->getHeaders(), - trim($matches[1]) - ) - ; + return json_decode(trim($matches[1]), true) ?: []; } return parent::getResponseContent($rawResponse); diff --git a/OAuth/ResourceOwner/SensioConnectResourceOwner.php b/OAuth/ResourceOwner/SensioConnectResourceOwner.php index 0dd45a263..3be06189f 100644 --- a/OAuth/ResourceOwner/SensioConnectResourceOwner.php +++ b/OAuth/ResourceOwner/SensioConnectResourceOwner.php @@ -11,8 +11,13 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\OAuth\Response\SensioConnectUserResponse; +use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * SensioConnectResourceOwner. @@ -23,6 +28,30 @@ */ class SensioConnectResourceOwner extends GenericOAuth2ResourceOwner { + /** + * {@inheritdoc} + */ + public function getUserInformation(array $accessToken, array $extraParameters = []) + { + $content = $this->doGetUserInformationRequest( + $this->normalizeUrl( + $this->options['infos_url'], + array_merge([$this->options['attr_name'] => $accessToken['access_token']], $extraParameters) + ) + ); + + try { + $response = $this->getUserResponse(); + $response->setData($content instanceof ResponseInterface ? $content->getContent(false) : $content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); + + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } + } + /** * {@inheritdoc} */ diff --git a/OAuth/ResourceOwner/SinaWeiboResourceOwner.php b/OAuth/ResourceOwner/SinaWeiboResourceOwner.php index 656e90326..108a28868 100644 --- a/OAuth/ResourceOwner/SinaWeiboResourceOwner.php +++ b/OAuth/ResourceOwner/SinaWeiboResourceOwner.php @@ -11,8 +11,12 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @final since 1.4 @@ -39,14 +43,18 @@ public function getUserInformation(array $accessToken = null, array $extraParame 'uid' => $accessToken['uid'], ]); - $content = $this->doGetUserInformationRequest($url)->getBody(); + try { + $content = $this->doGetUserInformationRequest($url); - $response = $this->getUserResponse(); - $response->setData((string) $content); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + $response = $this->getUserResponse(); + $response->setData($content instanceof ResponseInterface ? $content->toArray(false) : $content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - return $response; + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/SpotifyResourceOwner.php b/OAuth/ResourceOwner/SpotifyResourceOwner.php index a841ea7b6..c07d1e322 100644 --- a/OAuth/ResourceOwner/SpotifyResourceOwner.php +++ b/OAuth/ResourceOwner/SpotifyResourceOwner.php @@ -11,8 +11,12 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Janne Savolainen @@ -40,14 +44,18 @@ public function getUserInformation(array $accessToken = null, array $extraParame 'access_token' => $accessToken['access_token'], ]); - $content = $this->doGetUserInformationRequest($url)->getBody(); + try { + $content = $this->doGetUserInformationRequest($url); - $response = $this->getUserResponse(); - $response->setData((string) $content); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + $response = $this->getUserResponse(); + $response->setData($content instanceof ResponseInterface ? $content->toArray(false) : $content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - return $response; + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/StackExchangeResourceOwner.php b/OAuth/ResourceOwner/StackExchangeResourceOwner.php index d300371ef..f0ae8cc12 100644 --- a/OAuth/ResourceOwner/StackExchangeResourceOwner.php +++ b/OAuth/ResourceOwner/StackExchangeResourceOwner.php @@ -11,8 +11,12 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * StackExchangeResourceOwner. @@ -39,19 +43,23 @@ class StackExchangeResourceOwner extends GenericOAuth2ResourceOwner public function getUserInformation(array $accessToken, array $extraParameters = []) { $parameters = array_merge( - [$this->options['attr_name'] => $accessToken['access_token']], - ['site' => $this->options['site'], 'key' => $this->options['key']], - $extraParameters + [$this->options['attr_name'] => $accessToken['access_token']], + ['site' => $this->options['site'], 'key' => $this->options['key']], + $extraParameters ); - $content = $this->doGetUserInformationRequest($this->normalizeUrl($this->options['infos_url'], $parameters)); + try { + $content = $this->doGetUserInformationRequest($this->normalizeUrl($this->options['infos_url'], $parameters)); - $response = $this->getUserResponse(); - $response->setData((string) $content->getBody()); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + $response = $this->getUserResponse(); + $response->setData($content instanceof ResponseInterface ? $content->toArray(false) : $content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - return $response; + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/TraktResourceOwner.php b/OAuth/ResourceOwner/TraktResourceOwner.php index b9a585b93..c08430d32 100644 --- a/OAuth/ResourceOwner/TraktResourceOwner.php +++ b/OAuth/ResourceOwner/TraktResourceOwner.php @@ -11,8 +11,12 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * TraktResourceOwner. @@ -45,12 +49,16 @@ public function getUserInformation(array $accessToken, array $extraParameters = 'trakt-api-version' => 2, ]); - $response = $this->getUserResponse(); - $response->setData((string) $content->getBody()); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + try { + $response = $this->getUserResponse(); + $response->setData($content instanceof ResponseInterface ? $content->toArray(false) : $content); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - return $response; + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/OAuth/ResourceOwner/VkontakteResourceOwner.php b/OAuth/ResourceOwner/VkontakteResourceOwner.php index 896cba7a9..d361fd2eb 100644 --- a/OAuth/ResourceOwner/VkontakteResourceOwner.php +++ b/OAuth/ResourceOwner/VkontakteResourceOwner.php @@ -11,10 +11,12 @@ namespace HWI\Bundle\OAuthBundle\OAuth\ResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken; -use Psr\Http\Message\ResponseInterface; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; /** * VkontakteResourceOwner. @@ -52,25 +54,24 @@ public function getUserInformation(array $accessToken, array $extraParameters = 'v' => $this->options['api_version'], ]); - $content = $this->doGetUserInformationRequest($url); + try { + $response = $this->getUserResponse(); + $response->setResourceOwner($this); + $response->setOAuthToken(new OAuthToken($accessToken)); - $response = $this->getUserResponse(); - // This will translate string response into array - $response->setData($content instanceof ResponseInterface ? (string) $content->getBody() : $content); - $response->setResourceOwner($this); - $response->setOAuthToken(new OAuthToken($accessToken)); + $content = $this->doGetUserInformationRequest($url)->toArray(false); + $content['email'] = $accessToken['email'] ?? null; - $content = $response->getData(); - $content['email'] = $accessToken['email'] ?? null; - - $response->setData($content); + if (isset($content['response'][0]['screen_name'])) { + $content['response'][0]['nickname'] = $content['response'][0]['screen_name']; + } - if (!$response->getNickname() && isset($content['response'][0]['screen_name'])) { - $content['response'][0]['nickname'] = $content['response'][0]['screen_name']; $response->setData($content); - } - return $response; + return $response; + } catch (TransportExceptionInterface | JsonException $e) { + throw new HttpTransportException('Error while sending HTTP request', $this->getName(), $e->getCode(), $e); + } } /** diff --git a/Resources/config/http_client.xml b/Resources/config/http_client.xml deleted file mode 100644 index 334fb0539..000000000 --- a/Resources/config/http_client.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/Resources/config/oauth.xml b/Resources/config/oauth.xml index 0c6cb84c3..230d34872 100644 --- a/Resources/config/oauth.xml +++ b/Resources/config/oauth.xml @@ -116,11 +116,9 @@ - - + - diff --git a/Resources/doc/1-setting_up_the_bundle.md b/Resources/doc/1-setting_up_the_bundle.md index c12b4709f..fab677a38 100644 --- a/Resources/doc/1-setting_up_the_bundle.md +++ b/Resources/doc/1-setting_up_the_bundle.md @@ -3,15 +3,9 @@ Step 1: Setting up the bundle ### A) Add HWIOAuthBundle to your project ```bash -composer require hwi/oauth-bundle symfony/http-client nyholm/psr7 guzzlehttp/promises php-http/httplug-bundle +composer require hwi/oauth-bundle ``` -Why `symfony/http-client` and its dependencies? We are decoupled from any HTTP messaging client thanks to [HTTPlug](http://httplug.io/). - -Why `php-http/httplug-bundle`? This is the official [Symfony integration](https://packagist.org/packages/php-http/httplug-bundle) of HTTPlug. -This makes it possible to provide the required HTTP client and message factory with ease. -The dependency is optional but you will have to [provide your own services](internals/configuring_the_http_client.md) if you don't set it up. - If you use a recent version of Symfony supporting [Symfony Flex](https://symfony.com/doc/current/quick_tour/flex_recipes.html), when prompted, accept to execute the recipes coming from the contrib repository. You'll see an error at the end of the process, it's intended. Continue straight to the second step: [configuring resource owners (Facebook, GitHub, Google, Windows Live and others](2-configuring_resource_owners.md) to fix it. @@ -22,21 +16,17 @@ If you use an old version of Symfony, follow the instructions provided in the ne Enable the bundle in the kernel: ```php -// app/AppKernel.php +// src/Kernel.php public function registerBundles() { $bundles = [ // ... - new Http\HttplugBundle\HttplugBundle(), // If you require the php-http/httplug-bundle package. new HWI\Bundle\OAuthBundle\HWIOAuthBundle(), ]; } ``` -If you don't use `HttplugBundle`, you will have to -[configure your own client service](internals/configuring_the_http_client.md). - ### C) Import the routing Import the `redirect.xml` and `login.xml` routing files in your own routing file. diff --git a/Resources/doc/index.md b/Resources/doc/index.md index ec536ac07..0f5af7b02 100644 --- a/Resources/doc/index.md +++ b/Resources/doc/index.md @@ -14,7 +14,6 @@ Getting Started With HWIOAuthBundle ## Internals - [Response object & "paths" explanation](internals/response_object_and_paths.md) -- [Configuring the HTTP Client](internals/configuring_the_http_client.md) - [Reference configuration](internals/reference_configuration.md) - [Configuring an external Resource Owner](internals/external_resource_owner.md) diff --git a/Resources/doc/internals/configuring_the_http_client.md b/Resources/doc/internals/configuring_the_http_client.md deleted file mode 100644 index 3ba62dd18..000000000 --- a/Resources/doc/internals/configuring_the_http_client.md +++ /dev/null @@ -1,121 +0,0 @@ -Internals: Configuring the HTTP Client -====================================== -As you already noticed, HWIOAuthBundle depends on [Httplug](http://httplug.io) -a lightweight library for issuing HTTP requests. - -The HTTP client configuration does now directly rely on HttplugBundle if installed. -You can see all configuration options and plugins on the -[vendor documentation](http://docs.php-http.org/en/latest/integrations/symfony-bundle.html#usage). - -If you want to use a different Httplug client/factory than the default one(s), you can specify it: - -```yaml -# app/config/config.yml - -httplug: - clients: - default: - factory: 'httplug.factory.curl' - hwi_special: - factory: 'httplug.factory.guzzle6' - -hwi_oauth: - http: - client: httplug.client.hwi_special # Default to httplug.client.default - factory: httplug.stream_factory # Default to httplug.message_factory.default -``` - -If you don't want to use the HTTPlug Bundle, the client and factory services **must be specified**. - -## Differences since 0.6+ version - -On v0.5 and below, you were able to configure your HTTP client like that: - -```yaml -# app/config/config.yml - -hwi_oauth: - http_client: - timeout: 10 # Time in seconds, after library will shutdown request, by default: 5 - verify_peer: false # Setting allowing you to turn off SSL verification, by default: true - ignore_errors: false # Setting allowing you to easier debug request errors, by default: true - max_redirects: 1 # Number of HTTP redirection request after which library will shutdown request, - # by default: 5 - proxy: "example.com:8080" # String with proxy configuration for cURL connections, ignored by default. - # "" -> don't set proxy and will use proxy system - # "example.com:8080" -> set custom proxy - # ":" -> disable proxy usage, ignoring also proxy system and ENVIRONMENT variables -``` - -As we now use Httplug, the http configuration does not rely on our bundle anymore. -Still, you can always configure Guzzle6 (if you use this client) thanks to the -[Guzzle6 adapter](http://docs.php-http.org/en/latest/clients/guzzle6-adapter.html). - -### Configure with HttplugBundle - -You need to [install and configure](../1-setting_up_the_bundle.md) the HttplugBundle first. - -Then apply your configuration trough `httplug` section: - -```yaml -# app/config/config.yml - -httplug: - clients: - default: - factory: 'httplug.factory.curl' - hwi_special: - factory: 'httplug.factory.guzzle6' - config: # You pass here the Guzzle configuration, exactly like before. - timeout: 10 - verify: false - max_redirects: 1 - ignore_errors: false - proxy: "example.com:8080" - -hwi_oauth: - http: - client: httplug.client.hwi_special # Then you specify the special service to use. -``` - -And voilĂ ! You now have your custom Guzzle 6 service with the same configuration. - -### Configure manually - -If you don't want to use HttplugBundle, you can still configure Guzzle quite easily. - -First, you have to declare your Guzzle 6 adapter and message factory as services, with your custom configuration: - -```yaml -# app/config/services.yml - -services: - guzzle_client.hwi_special: - class: Http\Adapter\Guzzle6\Client - factory: - - Http\Adapter\Guzzle6\Client - - createWithConfig - arguments: - - timeout: 10 - verify_peer: false - max_redirects: 1 - ignore_errors: false - proxy: "example.com:8080" - guzzle_client.message_factory: - class: Http\Message\MessageFactory\GuzzleMessageFactory -``` - -Then configure the bundle to use the service created above: - -```yaml -# app/config/config.yml - -hwi_oauth: - http: - client: guzzle_client.hwi_special - message_factory: guzzle_client.message_factory -``` - -That's it. Now your custom Httplug Guzzle adapter service is used. - -[Return to the index.](../index.md) diff --git a/Resources/doc/resource_owners/fiware.md b/Resources/doc/resource_owners/fiware.md index c0c5d161b..73e0a143d 100644 --- a/Resources/doc/resource_owners/fiware.md +++ b/Resources/doc/resource_owners/fiware.md @@ -17,15 +17,6 @@ If you are using the FI-WARE Lab Cloud you will have to increase the timeout for ```yaml # app/config/config.yml - -# With php-http/httplug-bundle -httplug: - clients: - default: - factory: 'httplug.factory.guzzle6' - config: - timeout: 15 - hwi_oauth: resource_owners: any_name: diff --git a/Tests/App/config.yml b/Tests/App/config.yml index fe02a02f7..6d66d716b 100644 --- a/Tests/App/config.yml +++ b/Tests/App/config.yml @@ -59,12 +59,10 @@ services: HWI\Bundle\OAuthBundle\Tests\App\Form\RegistrationFormType: ~ HWI\Bundle\OAuthBundle\Tests\App\UserProvider: ~ - Psr\Http\Client\ClientInterface: + Symfony\Contracts\HttpClient\HttpClientInterface: public: true synthetic: true - Http\Message\MessageFactory\GuzzleMessageFactory: - doctrine: dbal: driver: pdo_sqlite @@ -79,9 +77,6 @@ hwi_oauth: account_connector: HWI\Bundle\OAuthBundle\Tests\App\UserProvider registration_form_handler: HWI\Bundle\OAuthBundle\Tests\App\Form\RegistrationFormHandler registration_form: HWI\Bundle\OAuthBundle\Tests\App\Form\RegistrationFormType - http: - client: Psr\Http\Client\ClientInterface - message_factory: Http\Message\MessageFactory\GuzzleMessageFactory firewall_names: [main] resource_owners: google: diff --git a/Tests/DependencyInjection/HWIOAuthExtensionTest.php b/Tests/DependencyInjection/HWIOAuthExtensionTest.php index 98c6f3567..26f69c71e 100644 --- a/Tests/DependencyInjection/HWIOAuthExtensionTest.php +++ b/Tests/DependencyInjection/HWIOAuthExtensionTest.php @@ -11,8 +11,6 @@ namespace HWI\Bundle\OAuthBundle\Tests\DependencyInjection; -use Http\Client\Common\HttpMethodsClient; -use Http\HttplugBundle\HttplugBundle; use HWI\Bundle\OAuthBundle\DependencyInjection\HWIOAuthExtension; use HWI\Bundle\OAuthBundle\Tests\Fixtures\MyCustomProvider; use PHPUnit\Framework\TestCase; @@ -36,10 +34,6 @@ protected function setUp(): void parent::setUp(); $this->containerBuilder = new ContainerBuilder(); - - $this->containerBuilder->setParameter('kernel.bundles', [ - 'HttplugBundle' => new HttplugBundle(), - ]); } protected function tearDown(): void @@ -48,16 +42,6 @@ protected function tearDown(): void unset($this->containerBuilder); } - public function testHttpClientExists() - { - $this->createEmptyConfiguration(); - - $this->assertHasDefinition( - 'hwi_oauth.http_client', - HttpMethodsClient::class - ); - } - public function testConfigurationThrowsExceptionUnlessFirewallNameSet() { $this->expectException(InvalidConfigurationException::class); @@ -491,10 +475,10 @@ public function testCreateResourceOwnerService() $this->assertEquals('hwi_oauth.abstract_resource_owner.oauth2', $definitions['hwi_oauth.resource_owner.my_github']->getParent()); $this->assertEquals('%hwi_oauth.resource_owner.github.class%', $definitions['hwi_oauth.resource_owner.my_github']->getClass()); - $argument2 = $definitions['hwi_oauth.resource_owner.my_github']->getArgument(2); + $argument2 = $definitions['hwi_oauth.resource_owner.my_github']->getArgument(1); $this->assertEquals('42', $argument2['client_id']); $this->assertEquals('foo', $argument2['client_secret']); - $this->assertEquals('my_github', $definitions['hwi_oauth.resource_owner.my_github']->getArgument(3)); + $this->assertEquals('my_github', $definitions['hwi_oauth.resource_owner.my_github']->getArgument(2)); } public function testCreateResourceOwnerServiceWithService() @@ -540,10 +524,10 @@ public function testCreateResourceOwnerServiceWithClass() $this->assertEquals('hwi_oauth.abstract_resource_owner.oauth2', $definitions['hwi_oauth.resource_owner.external_ressource_owner']->getParent()); $this->assertEquals(MyCustomProvider::class, $definitions['hwi_oauth.resource_owner.external_ressource_owner']->getClass()); - $argument2 = $definitions['hwi_oauth.resource_owner.external_ressource_owner']->getArgument(2); + $argument2 = $definitions['hwi_oauth.resource_owner.external_ressource_owner']->getArgument(1); $this->assertEquals('42', $argument2['client_id']); $this->assertEquals('foo', $argument2['client_secret']); - $this->assertEquals('external_ressource_owner', $definitions['hwi_oauth.resource_owner.external_ressource_owner']->getArgument(3)); + $this->assertEquals('external_ressource_owner', $definitions['hwi_oauth.resource_owner.external_ressource_owner']->getArgument(2)); } protected function createEmptyConfiguration() @@ -672,21 +656,12 @@ private function assertAlias(string $value, string $key): void $this->assertEquals($value, (string) $this->containerBuilder->getAlias($key), sprintf('%s alias is correct', $key)); } - private function assertParameter($value, string $key) + private function assertParameter($value, string $key): void { $this->assertEquals($value, $this->containerBuilder->getParameter($key), sprintf('%s parameter is correct', $key)); } - private function assertHasDefinition(string $id, ?string $className = null) - { - $this->assertTrue(($this->containerBuilder->hasDefinition($id) ?: $this->containerBuilder->hasAlias($id))); - - if (null !== $className) { - $this->assertSame($this->containerBuilder->findDefinition($id)->getClass(), $className); - } - } - - private function assertNotHasDefinition(string $id) + private function assertNotHasDefinition(string $id): void { $this->assertFalse($this->containerBuilder->hasDefinition($id) || $this->containerBuilder->hasAlias($id)); } diff --git a/Tests/Functional/Controller/ConnectControllerTest.php b/Tests/Functional/Controller/ConnectControllerTest.php index 8429ef61b..0dd3c3d97 100644 --- a/Tests/Functional/Controller/ConnectControllerTest.php +++ b/Tests/Functional/Controller/ConnectControllerTest.php @@ -18,14 +18,15 @@ use HWI\Bundle\OAuthBundle\Security\Core\Exception\AccountNotLinkedException; use HWI\Bundle\OAuthBundle\Tests\App\AppKernel; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomOAuthToken; -use Psr\Http\Client\ClientInterface; -use Psr\Http\Message\ResponseInterface; use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\BrowserKit\Cookie; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; final class ConnectControllerTest extends WebTestCase { @@ -41,19 +42,20 @@ public static function getKernelClass(): string public function testRegistration(): void { - $mockResponse = $this->createMock(ResponseInterface::class); - $mockResponse->method('getBody') - ->willReturn(json_encode(['access_token' => 'valid-access-token'])); - - $httpClient = $this->createMock(ClientInterface::class); - $httpClient->method('sendRequest') - ->withAnyParameters() - ->willReturn($mockResponse); + $httpClient = new MockHttpClient( + function ($method, $url, $options) { + return new MockResponse( + '{"access_token":"valid-access-token"}', + [ + 'response_headers' => ['content-type' => 'application/json'], + ] + ); + } + ); $client = static::createClient(); $client->disableReboot(); - - self::$container->set(ClientInterface::class, $httpClient); + $client->getContainer()->set(HttpClientInterface::class, $httpClient); $key = 1; $exception = new AccountNotLinkedException(); @@ -88,19 +90,20 @@ public function testRegistration(): void public function testConnectService(): void { - $response = $this->createMock(ResponseInterface::class); - $response->method('getBody') - ->willReturn(json_encode(['name' => 'foo'])); - - $httpClient = $this->createMock(ClientInterface::class); - $httpClient->method('sendRequest') - ->withAnyParameters() - ->willReturn($response); + $httpClient = new MockHttpClient( + function ($method, $url, $options) { + return new MockResponse( + '{"name":"foo"}', + [ + 'response_headers' => ['content-type' => 'application/json'], + ] + ); + } + ); $client = static::createClient(); $client->disableReboot(); - - self::$container->set(ClientInterface::class, $httpClient); + $client->getContainer()->set(HttpClientInterface::class, $httpClient); $this->createDatabase(); diff --git a/Tests/Functional/Controller/LoginControllerTest.php b/Tests/Functional/Controller/LoginControllerTest.php index 6b9d24d32..0053bc872 100644 --- a/Tests/Functional/Controller/LoginControllerTest.php +++ b/Tests/Functional/Controller/LoginControllerTest.php @@ -15,14 +15,15 @@ use HWI\Bundle\OAuthBundle\Security\Core\Exception\AccountNotLinkedException; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomOAuthToken; -use Psr\Http\Client\ClientInterface; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\BrowserKit\Cookie; +use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\Security; +use Symfony\Contracts\HttpClient\HttpClientInterface; final class LoginControllerTest extends WebTestCase { @@ -30,7 +31,7 @@ public function testLoginPage(): void { $client = static::createClient(); - self::$container->set(ClientInterface::class, $this->createMock(ClientInterface::class)); + self::$container->set(HttpClientInterface::class, $this->createMock(HttpClientInterface::class)); $crawler = $client->request('GET', '/login_hwi/'); @@ -57,11 +58,12 @@ public function testRedirectingToRegistrationFormWithError(): void public function testLoginPageWithError(): void { - $client = static::createClient(); + $httpClient = new MockHttpClient(); - self::$container->set(ClientInterface::class, $this->createMock(ClientInterface::class)); + $client = static::createClient(); + $client->getContainer()->set(HttpClientInterface::class, $httpClient); - $session = $this->getSession(); + $session = $client->getContainer()->get('session'); $this->logIn($client, $session); $exception = new UsernameNotFoundException(); diff --git a/Tests/Functional/IntegrationTest.php b/Tests/Functional/IntegrationTest.php index 8f525ec32..12f8a5052 100644 --- a/Tests/Functional/IntegrationTest.php +++ b/Tests/Functional/IntegrationTest.php @@ -13,10 +13,11 @@ namespace HWI\Bundle\OAuthBundle\Tests\Functional; -use Psr\Http\Client\ClientInterface; -use Psr\Http\Message\ResponseInterface; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\HttpClient\HttpClientInterface; class IntegrationTest extends WebTestCase { @@ -38,8 +39,7 @@ public function testRequestRedirect(): void $this->assertSame(200, $response->getStatusCode(), 'No landing, got redirect to '.$response->headers->get('Location')); $client->disableReboot(); - - self::$container->set(ClientInterface::class, $this->createMock(ClientInterface::class)); + $client->getContainer()->set(HttpClientInterface::class, new MockHttpClient()); $client->click($crawler->selectLink('Login')->link()); @@ -68,19 +68,20 @@ public function testRequestCheck(): void 'prompt' => 'none', ]); - $response = $this->createMock(ResponseInterface::class); - $response->method('getBody') - ->willReturn(json_encode(['access_token' => 'valid-access-token'])); - - $httpClient = $this->createMock(ClientInterface::class); - $httpClient->method('sendRequest') - ->withAnyParameters() - ->willReturn($response); + $httpClient = new MockHttpClient( + function ($method, $url, $options) { + return new MockResponse( + '{"access_token":"valid-access-token"}', + [ + 'response_headers' => ['content-type' => 'application/json'], + ] + ); + } + ); $client = static::createClient(); $client->disableReboot(); - - self::$container->set(ClientInterface::class, $httpClient); + $client->getContainer()->set(HttpClientInterface::class, $httpClient); $client->request('GET', $redirectLoginFromService); diff --git a/Tests/OAuth/ResourceOwner/AppleResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/AppleResourceOwnerTest.php index 9fc94e070..91028681d 100644 --- a/Tests/OAuth/ResourceOwner/AppleResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/AppleResourceOwnerTest.php @@ -12,8 +12,10 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\AppleResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Exception\AuthenticationException; class AppleResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { @@ -37,36 +39,44 @@ class AppleResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testHandleRequest() { + $resourceOwner = $this->createResourceOwner(); + $request = new Request(['test' => 'test']); - $this->assertFalse($this->resourceOwner->handles($request)); + $this->assertFalse($resourceOwner->handles($request)); $request = new Request(['code' => 'test']); - $this->assertFalse($this->resourceOwner->handles($request)); + $this->assertFalse($resourceOwner->handles($request)); $request = new Request([], ['code' => 'test']); - $this->assertTrue($this->resourceOwner->handles($request)); + $this->assertTrue($resourceOwner->handles($request)); $request = new Request([], ['code' => 'test', 'test' => 'test']); - $this->assertTrue($this->resourceOwner->handles($request)); + $this->assertTrue($resourceOwner->handles($request)); } public function testGetAccessTokenFailedResponse() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); - $this->mockHttpClient('{"error": {"message": "invalid"}}', 'application/json; charset=utf-8'); $request = new Request(['code' => 'code']); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"error": {"message": "invalid"}}', 'application/json; charset=utf-8'), + ] + ); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } public function testDisplayPopup() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['display' => 'popup']); + $resourceOwner = $this->createResourceOwner(['display' => 'popup']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&scope=name+email&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&response_mode=form_post', @@ -74,31 +84,19 @@ public function testDisplayPopup() ); } - public function testRevokeToken() - { - $this->httpResponseHttpCode = 200; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); - - $this->assertTrue($this->resourceOwner->revokeToken('token')); - } - - public function testRevokeTokenFails() - { - $this->httpResponseHttpCode = 401; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); - - $this->assertFalse($this->resourceOwner->revokeToken('token')); - } - public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['user_response_class' => $class]); - - $token = '.'.base64_encode($this->userResponse); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** @var CustomUserResponse $userResponse */ - $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token', 'id_token' => $token]); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token', 'id_token' => '.'.base64_encode($this->userResponse)]); $this->assertInstanceOf($class, $userResponse); $this->assertEquals('foo666', $userResponse->getUsername()); @@ -114,10 +112,18 @@ public function testGetUserInformation() { $token = '.'.base64_encode($this->userResponse); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); + /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation([ + $userResponse = $resourceOwner->getUserInformation([ 'access_token' => 'token', 'id_token' => $token, 'firstName' => 'Test', @@ -132,7 +138,7 @@ public function testGetUserInformation() $this->assertNull($userResponse->getRefreshToken()); $this->assertNull($userResponse->getExpiresIn()); - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token', 'id_token' => $token]); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token', 'id_token' => $token]); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('localhost@gmail.com', $userResponse->getEmail()); $this->assertEquals('token', $userResponse->getAccessToken()); @@ -144,13 +150,16 @@ public function testGetUserInformation() public function testGetUserInformationFailure() { - $exception = new \Exception('Undefined index id_token'); - - try { - $this->resourceOwner->getUserInformation(['access_token' => 'token']); - $this->fail('An exception should have been raised'); - } catch (\Exception $e) { - $this->assertSame($exception->getMessage(), $e->getMessage()); - } + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Undefined index id_token'); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); + $resourceOwner->getUserInformation(['access_token' => 'token']); } } diff --git a/Tests/OAuth/ResourceOwner/Auth0ResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/Auth0ResourceOwnerTest.php index a013b88f2..39913aca9 100644 --- a/Tests/OAuth/ResourceOwner/Auth0ResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/Auth0ResourceOwnerTest.php @@ -11,10 +11,8 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; -use Http\Discovery\MessageFactoryDiscovery; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\Auth0ResourceOwner; -use Psr\Http\Message\RequestInterface; -use Symfony\Component\HttpFoundation\Request; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; use Symfony\Component\Security\Http\HttpUtils; class Auth0ResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -50,60 +48,7 @@ class Auth0ResourceOwnerTest extends GenericOAuth2ResourceOwnerTest protected $authorizationUrlBasePart = 'https://example.oauth0.com/authorize?auth0Client=eyJuYW1lIjoiSFdJT0F1dGhCdW5kbGUiLCJ2ZXJzaW9uIjoidW5rbm93biIsImVudmlyb25tZW50Ijp7Im5hbWUiOiJQSFAiLCJ2ZXJzaW9uIjoiRkFLRV9QSFBfVkVSU0lPTl9GT1JfVEVTVFMifX0=&response_type=code&client_id=clientid'; - /** - * Tests if {@see Auth0ResourceOwner::getAccessToken} would send the expected request to Auth0. - */ - public function testGetAccessTokenSendsExpectedRequest(): void - { - $expectedRequestUri = 'https://example.oauth0.com/oauth/token'; - $expectedRequestMethod = 'POST'; - $expectedAuthorizationHeader = [ - 'Basic Y2xpZW50aWQ6Y2xpZW50c2VjcmV0', - ]; - $expectedAuth0ClientRequestHeader = [ - 'eyJuYW1lIjoiSFdJT0F1dGhCdW5kbGUiLCJ2ZXJzaW9uIjoidW5rbm93biIsImVudmlyb25tZW50Ijp7Im5hbWUiOiJQSFAiLCJ2ZXJzaW9uIjoiRkFLRV9QSFBfVkVSU0lPTl9GT1JfVEVTVFMifX0=', - ]; - $expectedRequestBodyContents = 'code=somecode&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fredirect.to%2F'; - - $this->mockHttpClientSendRequestWithRequestAssertions( - $expectedRequestUri, - $expectedRequestMethod, - $expectedAuthorizationHeader, - $expectedAuth0ClientRequestHeader, - $expectedRequestBodyContents - ); - - $request = new Request(['code' => 'somecode']); - - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); - } - - /** - * Tests if {@see Auth0ResourceOwner::getUserInformation} would send the expected request to Auth0. - */ - public function testGetUserInformationSendsExpectedRequest(): void - { - $expectedRequestUri = 'https://example.oauth0.com/userinfo'; - $expectedRequestMethod = 'GET'; - $expectedAuthorizationHeader = [ - 'Bearer token', - ]; - $expectedAuth0ClientRequestHeader = [ - 'eyJuYW1lIjoiSFdJT0F1dGhCdW5kbGUiLCJ2ZXJzaW9uIjoidW5rbm93biIsImVudmlyb25tZW50Ijp7Im5hbWUiOiJQSFAiLCJ2ZXJzaW9uIjoiRkFLRV9QSFBfVkVSU0lPTl9GT1JfVEVTVFMifX0=', - ]; - - $this->mockHttpClientSendRequestWithRequestAssertions( - $expectedRequestUri, - $expectedRequestMethod, - $expectedAuthorizationHeader, - $expectedAuth0ClientRequestHeader, - '' - ); - - $this->resourceOwner->getUserInformation($this->tokenData); - } - - protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $options) + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface { $auth0Client = base64_encode(json_encode([ 'name' => 'HWIOAuthBundle', @@ -114,67 +59,20 @@ protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $option ], ])); - $options = array_merge( - $options, - [ - 'authorization_url' => '{base_url}/authorize?auth0Client='.$auth0Client, - 'access_token_url' => '{base_url}/oauth/token', - 'infos_url' => '{base_url}/userinfo', - 'auth0_client' => $auth0Client, - 'base_url' => 'https://example.oauth0.com', - ] + return parent::setUpResourceOwner( + $name, + $httpUtils, + array_merge( + $options, + [ + 'authorization_url' => '{base_url}/authorize?auth0Client='.$auth0Client, + 'access_token_url' => '{base_url}/oauth/token', + 'infos_url' => '{base_url}/userinfo', + 'auth0_client' => $auth0Client, + 'base_url' => 'https://example.oauth0.com', + ] + ), + $responses ); - - return parent::setUpResourceOwner($name, $httpUtils, $options); - } - - /** - * Mocks the {@see HttpClient::sendRequest} method with assertions on the request. - */ - private function mockHttpClientSendRequestWithRequestAssertions( - string $expectedRequestUri, - string $expectedRequestMethod, - array $expectedAuthorizationHeader, - array $expectedAuth0ClientRequestHeader, - string $expectedRequestBodyContents - ): void { - $this->httpClient->expects($this->once()) - ->method('sendRequest') - ->with( - $this->callback(function (RequestInterface $request) use ($expectedRequestUri, $expectedRequestMethod, $expectedAuthorizationHeader, $expectedAuth0ClientRequestHeader, $expectedRequestBodyContents) { - $this->assertEquals($expectedRequestUri, $request->getUri()); - $this->assertSame( - $expectedRequestMethod, - $request->getMethod(), - 'The request should be send with the expected request method.' - ); - $this->assertSame( - $expectedAuthorizationHeader, - $request->getHeader('Authorization'), - 'The Authorization header should be added to the request with the Base64 encoded client_id and client_secret or with a bearer token.' - ); - $this->assertSame( - $expectedAuth0ClientRequestHeader, - $request->getHeader('Auth0-Client'), - 'The Auth0-Client header should be added with the expected Base64 encoded version information.' - ); - $this->assertSame( - $expectedRequestBodyContents, - $request->getBody()->getContents(), - 'The request body should contain the expected content / variables.' - ); - - return true; - }) - ) - ->willReturnCallback(function (RequestInterface $request) { - return MessageFactoryDiscovery::find() - ->createResponse( - $this->httpResponseHttpCode, - null, - $request->withAddedHeader('Content-Type', 'application/json')->getHeaders(), - '{"access_token": "code"}' - ); - }); } } diff --git a/Tests/OAuth/ResourceOwner/AzureResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/AzureResourceOwnerTest.php index 69dba4651..0c53a3222 100644 --- a/Tests/OAuth/ResourceOwner/AzureResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/AzureResourceOwnerTest.php @@ -11,9 +11,9 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; -use Http\Client\Exception\TransferException; use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\AzureResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; class AzureResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -44,13 +44,20 @@ class AzureResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $token = '.'.base64_encode($this->userResponse); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); + /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation([ + $userResponse = $resourceOwner->getUserInformation([ 'access_token' => 'token', - 'id_token' => $token, + 'id_token' => '.'.base64_encode($this->userResponse), ]); $this->assertEquals('1', $userResponse->getUsername()); @@ -65,16 +72,20 @@ public function testGetUserInformation() public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['user_response_class' => $class]); - - $token = '.'.base64_encode($this->userResponse); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ $userResponse = $resourceOwner->getUserInformation([ 'access_token' => 'token', - 'id_token' => $token, + 'id_token' => '.'.base64_encode($this->userResponse), ]); $this->assertInstanceOf($class, $userResponse); @@ -89,19 +100,15 @@ public function testCustomResponseClass() public function testGetUserInformationFailure() { - $exception = new TransferException(); - - $this->httpClient->expects($this->once()) - ->method('sendRequest') - ->will($this->throwException($exception)); - - $token = '.'.base64_encode($this->userResponse); - - try { - $this->resourceOwner->getUserInformation(['access_token' => 'token', 'id_token' => $token]); - $this->fail('An exception should have been raised'); - } catch (HttpTransportException $e) { - $this->assertSame($exception, $e->getPrevious()); - } + $this->expectException(HttpTransportException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('', null, 401), + ] + ); + $resourceOwner->getUserInformation(['access_token' => 'token', 'id_token' => '.'.base64_encode($this->userResponse)]); } } diff --git a/Tests/OAuth/ResourceOwner/BitbucketResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/BitbucketResourceOwnerTest.php index e03699426..019212a5c 100644 --- a/Tests/OAuth/ResourceOwner/BitbucketResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/BitbucketResourceOwnerTest.php @@ -32,10 +32,16 @@ class BitbucketResourceOwnerTest extends GenericOAuth1ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); $accessToken = ['oauth_token' => 'token', 'oauth_token_secret' => 'secret']; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('1', $userResponse->getNickname()); diff --git a/Tests/OAuth/ResourceOwner/BoxResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/BoxResourceOwnerTest.php index 85aff1ccd..d23dbb746 100644 --- a/Tests/OAuth/ResourceOwner/BoxResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/BoxResourceOwnerTest.php @@ -35,17 +35,27 @@ class BoxResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testRevokeToken() { - $this->httpResponseHttpCode = 200; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); - - $this->assertTrue($this->resourceOwner->revokeToken('token')); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"access_token": "bar"}', 'application/json'), + ] + ); + + $this->assertTrue($resourceOwner->revokeToken('token')); } public function testRevokeTokenFails() { - $this->httpResponseHttpCode = 401; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); - - $this->assertFalse($this->resourceOwner->revokeToken('token')); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"access_token": "bar"}', 'application/json', 401), + ] + ); + + $this->assertFalse($resourceOwner->revokeToken('token')); } } diff --git a/Tests/OAuth/ResourceOwner/BufferAppResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/BufferAppResourceOwnerTest.php index 58d6177aa..50cf64fd4 100644 --- a/Tests/OAuth/ResourceOwner/BufferAppResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/BufferAppResourceOwnerTest.php @@ -12,6 +12,8 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\BufferAppResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; +use Symfony\Component\Security\Core\Exception\AuthenticationException; class BufferAppResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { @@ -31,12 +33,18 @@ class BufferAppResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('4f0c0a06512f7ef214000000', $userResponse->getUsername()); $this->assertEquals('4f0c0a06512f7ef214000000', $userResponse->getNickname()); @@ -47,4 +55,19 @@ public function testGetUserInformation() $this->assertNull($userResponse->getRefreshToken()); $this->assertNull($userResponse->getExpiresIn()); } + + public function testGetUserInformationFailure() + { + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid', 'application/json; charset=utf-8', 401), + ] + ); + + $resourceOwner->getUserInformation($this->tokenData); + } } diff --git a/Tests/OAuth/ResourceOwner/CleverResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/CleverResourceOwnerTest.php index d2cf88bb0..a839d1973 100644 --- a/Tests/OAuth/ResourceOwner/CleverResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/CleverResourceOwnerTest.php @@ -14,17 +14,9 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\CleverResourceOwner; /** - * CleverResourceOwnerTest. - * * @author Matt Farmer */ class CleverResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { protected $resourceOwnerClass = CleverResourceOwner::class; - - protected function setUp(): void - { - $this->resourceOwnerName = 'CleverResourceOwner'; - $this->resourceOwner = $this->createResourceOwner($this->resourceOwnerName); - } } diff --git a/Tests/OAuth/ResourceOwner/DailymotionResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/DailymotionResourceOwnerTest.php index d180dea73..d3737dded 100644 --- a/Tests/OAuth/ResourceOwner/DailymotionResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/DailymotionResourceOwnerTest.php @@ -31,7 +31,7 @@ class DailymotionResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testDisplayPopup() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['display' => 'popup']); + $resourceOwner = $this->createResourceOwner(['display' => 'popup']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&display=popup', @@ -43,6 +43,6 @@ public function testInvalidDisplayOptionValueThrowsException() { $this->expectException(\Symfony\Component\OptionsResolver\Exception\ExceptionInterface::class); - $this->createResourceOwner($this->resourceOwnerName, ['display' => 'invalid']); + $this->createResourceOwner(['display' => 'invalid']); } } diff --git a/Tests/OAuth/ResourceOwner/DeezerResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/DeezerResourceOwnerTest.php index 3131ba249..a8c705a18 100644 --- a/Tests/OAuth/ResourceOwner/DeezerResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/DeezerResourceOwnerTest.php @@ -53,9 +53,15 @@ class DeezerResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('passkey', $userResponse->getNickname()); $this->assertEquals('Kieu', $userResponse->getRealName()); diff --git a/Tests/OAuth/ResourceOwner/DeviantartResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/DeviantartResourceOwnerTest.php index 47fe1e30b..b590c8ccd 100644 --- a/Tests/OAuth/ResourceOwner/DeviantartResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/DeviantartResourceOwnerTest.php @@ -12,6 +12,7 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\DeviantartResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; class DeviantartResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { @@ -31,12 +32,18 @@ class DeviantartResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('kouiskas', $userResponse->getUsername()); $this->assertEquals('kouiskas', $userResponse->getNickname()); diff --git a/Tests/OAuth/ResourceOwner/DropboxResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/DropboxResourceOwnerTest.php index c7c522b1c..2c2bb0812 100644 --- a/Tests/OAuth/ResourceOwner/DropboxResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/DropboxResourceOwnerTest.php @@ -26,9 +26,11 @@ class DropboxResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetAuthorizationUrl() { + $resourceOwner = $this->createResourceOwner(); + $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F', - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/') + $resourceOwner->getAuthorizationUrl('http://redirect.to/') ); } } diff --git a/Tests/OAuth/ResourceOwner/EventbriteResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/EventbriteResourceOwnerTest.php index e233f3e69..e3e027b3a 100644 --- a/Tests/OAuth/ResourceOwner/EventbriteResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/EventbriteResourceOwnerTest.php @@ -38,12 +38,18 @@ class EventbriteResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformationFirstAndLastName() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); /** * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('bar', $userResponse->getFirstName()); $this->assertEquals('foo', $userResponse->getLastName()); diff --git a/Tests/OAuth/ResourceOwner/FacebookResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/FacebookResourceOwnerTest.php index 6d741c5b3..f1505faec 100644 --- a/Tests/OAuth/ResourceOwner/FacebookResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/FacebookResourceOwnerTest.php @@ -13,6 +13,7 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\FacebookResourceOwner; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\OptionsResolver\Exception\ExceptionInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; class FacebookResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -39,15 +40,22 @@ public function testGetAccessTokenFailedResponse() { $this->expectException(AuthenticationException::class); - $this->mockHttpClient('{"error": {"message": "invalid"}}', 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"error": {"message": "invalid"}}', 'application/json; charset=utf-8'), + ] + ); + $request = new Request(['code' => 'code']); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } public function testAuthTypeRerequest() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['auth_type' => 'rerequest']); + $resourceOwner = $this->createResourceOwner(['auth_type' => 'rerequest']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&auth_type=rerequest', @@ -57,7 +65,7 @@ public function testAuthTypeRerequest() public function testAuthTypeRerequestAndDisplayPopup() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['display' => 'popup', 'auth_type' => 'rerequest']); + $resourceOwner = $this->createResourceOwner(['display' => 'popup', 'auth_type' => 'rerequest']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&display=popup&auth_type=rerequest', @@ -67,7 +75,7 @@ public function testAuthTypeRerequestAndDisplayPopup() public function testDisplayPopup() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['display' => 'popup']); + $resourceOwner = $this->createResourceOwner(['display' => 'popup']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&display=popup', @@ -77,38 +85,47 @@ public function testDisplayPopup() public function testInvalidDisplayOptionValueThrowsException() { - $this->expectException(\Symfony\Component\OptionsResolver\Exception\ExceptionInterface::class); + $this->expectException(ExceptionInterface::class); - $this->createResourceOwner($this->resourceOwnerName, ['display' => 'invalid']); + $this->createResourceOwner(['display' => 'invalid']); } public function testRevokeToken() { - $this->httpResponseHttpCode = 200; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"access_token": "bar"}', 'application/json'), + ] + ); - $this->assertTrue($this->resourceOwner->revokeToken('token')); + $this->assertTrue($resourceOwner->revokeToken('token')); } public function testRevokeTokenFails() { - $this->httpResponseHttpCode = 401; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"access_token": "bar"}', 'application/json', 401), + ] + ); - $this->assertFalse($this->resourceOwner->revokeToken('token')); + $this->assertFalse($resourceOwner->revokeToken('token')); } public function testGetAccessTokenErrorResponse() { $this->expectException(AuthenticationException::class); - $this->mockHttpClient(); - $request = new Request([ 'error_code' => 901, 'error_message' => 'This app is in sandbox mode. Edit the app configuration at http://developers.facebook.com/apps to make the app publicly visible.', ]); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner = $this->createResourceOwner(); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } } diff --git a/Tests/OAuth/ResourceOwner/FlickrResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/FlickrResourceOwnerTest.php index dbc3333e8..b8d758b71 100644 --- a/Tests/OAuth/ResourceOwner/FlickrResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/FlickrResourceOwnerTest.php @@ -28,6 +28,14 @@ class FlickrResourceOwnerTest extends GenericOAuth1ResourceOwnerTest */ public function testGetUserInformation() { + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); + $accessToken = [ 'oauth_token' => 'token', 'oauth_token_secret' => 'secret', @@ -35,7 +43,7 @@ public function testGetUserInformation() 'user_nsid' => '15362483@N08', 'username' => 'lakiboy83', ]; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('15362483@N08', $userResponse->getUsername()); $this->assertEquals('lakiboy83', $userResponse->getNickname()); @@ -50,16 +58,22 @@ public function testGetUserInformation() */ public function testGetAuthorizationUrlContainOAuthTokenAndSecret() { - $this->mockHttpClient('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json; charset=utf-8'), + ] + ); $this->storage->expects($this->once()) ->method('save') - ->with($this->resourceOwner, ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'timestamp' => time()]) + ->with($resourceOwner, ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'timestamp' => time()]) ; $this->assertEquals( $this->options['authorization_url'].'&oauth_token=token&perms=read&nojsoncallback=1', - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/') + $resourceOwner->getAuthorizationUrl('http://redirect.to/') ); } @@ -69,7 +83,7 @@ public function testGetAuthorizationUrlContainOAuthTokenAndSecret() public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['user_response_class' => $class]); + $resourceOwner = $this->createResourceOwner(['user_response_class' => $class]); /* @var $userResponse CustomUserResponse */ $userResponse = $resourceOwner->getUserInformation(['oauth_token' => 'token', 'oauth_token_secret' => 'secret']); diff --git a/Tests/OAuth/ResourceOwner/FoursquareResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/FoursquareResourceOwnerTest.php index d1815367f..c606c6277 100644 --- a/Tests/OAuth/ResourceOwner/FoursquareResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/FoursquareResourceOwnerTest.php @@ -39,12 +39,18 @@ class FoursquareResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformationFirstAndLastName() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); /** * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('bar', $userResponse->getFirstName()); $this->assertEquals('foo', $userResponse->getLastName()); diff --git a/Tests/OAuth/ResourceOwner/GenericOAuth1ResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/GenericOAuth1ResourceOwnerTest.php index 2d6d04fa7..a5cf21e4e 100644 --- a/Tests/OAuth/ResourceOwner/GenericOAuth1ResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/GenericOAuth1ResourceOwnerTest.php @@ -11,24 +11,18 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; -use Http\Client\HttpClient; use HWI\Bundle\OAuthBundle\OAuth\RequestDataStorageInterface; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\GenericOAuth1ResourceOwner; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\OptionsResolver\Exception\ExceptionInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; class GenericOAuth1ResourceOwnerTest extends ResourceOwnerTestCase { protected $resourceOwnerClass = GenericOAuth1ResourceOwner::class; - /** @var GenericOAuth1ResourceOwner */ - protected $resourceOwner; - protected $resourceOwnerName; - /** @var MockObject&HttpClient */ - protected $httpClient; - protected $httpResponse; - protected $httpResponseContentType; + /** @var MockObject&RequestDataStorageInterface */ protected $storage; @@ -50,47 +44,49 @@ class GenericOAuth1ResourceOwnerTest extends ResourceOwnerTestCase 'realname' => 'foo_disp', ]; - protected function setUp(): void - { - $this->resourceOwnerName = str_replace(['generic', 'resourceownertest'], '', strtolower(__CLASS__)); - $this->resourceOwner = $this->createResourceOwner($this->resourceOwnerName); - } - public function testUndefinedOptionThrowsException() { $this->expectException(ExceptionInterface::class); - $this->createResourceOwner($this->resourceOwnerName, ['non_existing' => null]); + $this->createResourceOwner(['non_existing' => null]); } public function testInvalidOptionValueThrowsException() { $this->expectException(ExceptionInterface::class); - $this->createResourceOwner($this->resourceOwnerName, ['csrf' => 'invalid']); + $this->createResourceOwner(['csrf' => 'invalid']); } public function testHandleRequest() { $request = new Request(['test' => 'test']); - $this->assertFalse($this->resourceOwner->handles($request)); + $resourceOwner = $this->createResourceOwner(); + + $this->assertFalse($resourceOwner->handles($request)); $request = new Request(['oauth_token' => 'test']); - $this->assertTrue($this->resourceOwner->handles($request)); + $this->assertTrue($resourceOwner->handles($request)); $request = new Request(['oauth_token' => 'test', 'test' => 'test']); - $this->assertTrue($this->resourceOwner->handles($request)); + $this->assertTrue($resourceOwner->handles($request)); } public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); - $accessToken = ['oauth_token' => 'token', 'oauth_token_secret' => 'secret']; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('bar', $userResponse->getNickname()); @@ -101,122 +97,176 @@ public function testGetUserInformation() public function testGetAuthorizationUrlContainOAuthTokenAndSecret() { - $this->mockHttpClient('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json; charset=utf-8'), + ] + ); $this->storage->expects($this->once()) ->method('save') - ->with($this->resourceOwner, ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'timestamp' => time()]); + ->with($resourceOwner, ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'timestamp' => time()]); $this->assertEquals( $this->options['authorization_url'].'&oauth_token=token', - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/') + $resourceOwner->getAuthorizationUrl('http://redirect.to/') ); } public function testGetAuthorizationUrlFailedResponseContainOnlyOAuthToken() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('{"oauth_token": "token"}', 'application/json; charset=utf-8'); + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"oauth_token": "token"}', 'application/json; charset=utf-8'), + ] + ); $this->storage->expects($this->never()) ->method('save'); - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/'); + $resourceOwner->getAuthorizationUrl('http://redirect.to/'); } public function testGetAuthorizationUrlFailedResponseContainOAuthProblem() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('oauth_problem=message'); + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('oauth_problem=message', 'text/plain'), + ] + ); $this->storage->expects($this->never()) ->method('save'); - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/'); + $resourceOwner->getAuthorizationUrl('http://redirect.to/'); } public function testGetAuthorizationUrlFailedResponseContainCallbackNotConfirmed() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('oauth_callback_confirmed=false'); + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('oauth_callback_confirmed=false', 'text/plain'), + ] + ); $this->storage->expects($this->never()) ->method('save'); - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/'); + $resourceOwner->getAuthorizationUrl('http://redirect.to/'); } public function testGetAuthorizationUrlFailedResponseNotContainOAuthTokenOrSecret() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('invalid'); + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid', 'text/plain'), + ] + ); $this->storage->expects($this->never()) ->method('save'); - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/'); + $resourceOwner->getAuthorizationUrl('http://redirect.to/'); } public function testGetAccessToken() { - $this->mockHttpClient('oauth_token=token&oauth_token_secret=secret'); - $request = new Request(['oauth_verifier' => 'code', 'oauth_token' => 'token']); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('oauth_token=token&oauth_token_secret=secret', 'text/plain'), + ] + ); + $this->storage->expects($this->once()) ->method('fetch') - ->with($this->resourceOwner, 'token') + ->with($resourceOwner, 'token') ->willReturn(['oauth_token' => 'token2', 'oauth_token_secret' => 'secret2']); $this->assertEquals( ['oauth_token' => 'token', 'oauth_token_secret' => 'secret'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') + $resourceOwner->getAccessToken($request, 'http://redirect.to/') ); } public function testGetAccessTokenJsonResponse() { - $this->mockHttpClient('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json'), + ] + ); $request = new Request(['oauth_verifier' => 'code', 'oauth_token' => 'token']); $this->storage->expects($this->once()) ->method('fetch') - ->with($this->resourceOwner, 'token') + ->with($resourceOwner, 'token') ->willReturn(['oauth_token' => 'token2', 'oauth_token_secret' => 'secret2']); $this->assertEquals( ['oauth_token' => 'token', 'oauth_token_secret' => 'secret'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') + $resourceOwner->getAccessToken($request, 'http://redirect.to/') ); } public function testGetAccessTokenJsonCharsetResponse() { - $this->mockHttpClient('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json; charset=utf-8'), + ] + ); $request = new Request(['oauth_verifier' => 'code', 'oauth_token' => 'token']); $this->storage->expects($this->once()) ->method('fetch') - ->with($this->resourceOwner, 'token') + ->with($resourceOwner, 'token') ->willReturn(['oauth_token' => 'token2', 'oauth_token_secret' => 'secret2']); $this->assertEquals( ['oauth_token' => 'token', 'oauth_token_secret' => 'secret'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') + $resourceOwner->getAccessToken($request, 'http://redirect.to/') ); } public function testGetAccessTokenFailedResponse() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('invalid'); + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid', 'text/plain'), + ] + ); $this->storage->expects($this->once()) ->method('fetch') @@ -227,14 +277,20 @@ public function testGetAccessTokenFailedResponse() $request = new Request(['oauth_token' => 'token', 'oauth_verifier' => 'code']); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } public function testGetAccessTokenErrorResponse() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('error=foo'); + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('error=foo', 'text/plain'), + ] + ); $this->storage->expects($this->once()) ->method('fetch') @@ -245,53 +301,56 @@ public function testGetAccessTokenErrorResponse() $request = new Request(['oauth_token' => 'token', 'oauth_verifier' => 'code']); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } public function testGetAccessTokenInvalidArgumentException() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner(); $this->storage->expects($this->once()) ->method('fetch') - ->will($this->throwException(new \InvalidArgumentException())); - - $this->httpClient->expects($this->never()) - ->method('sendRequest'); + ->willThrowException(new \InvalidArgumentException()); $this->storage->expects($this->never()) ->method('save'); $request = new Request(['oauth_token' => 'token', 'oauth_verifier' => 'code']); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } public function testRefreshAccessToken() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); - $this->resourceOwner->refreshAccessToken('token'); + $resourceOwner = $this->createResourceOwner(); + $resourceOwner->refreshAccessToken('token'); } public function testRevokeToken() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); - $this->resourceOwner->revokeToken('token'); + $resourceOwner = $this->createResourceOwner(); + $resourceOwner->revokeToken('token'); } public function testCsrfTokenIsAlwaysValidForOAuth1() { + $resourceOwner = $this->createResourceOwner(); + $this->storage->expects($this->never()) ->method('fetch'); - $this->assertTrue($this->resourceOwner->isCsrfTokenValid('valid_token')); + $this->assertTrue($resourceOwner->isCsrfTokenValid('valid_token')); } public function testCsrfTokenValid() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['csrf' => true]); + $resourceOwner = $this->createResourceOwner(['csrf' => true]); $this->storage->expects($this->never()) ->method('fetch'); @@ -301,17 +360,23 @@ public function testCsrfTokenValid() public function testGetSetName() { - $this->assertEquals($this->resourceOwnerName, $this->resourceOwner->getName()); - $this->resourceOwner->setName('foo'); - $this->assertEquals('foo', $this->resourceOwner->getName()); + $resourceOwner = $this->createResourceOwner(); + $this->assertEquals($this->prepareResourceOwnerName(), $resourceOwner->getName()); + + $resourceOwner->setName('foo'); + $this->assertEquals('foo', $resourceOwner->getName()); } public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['user_response_class' => $class]); - - $this->mockHttpClient(); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** @var CustomUserResponse $userResponse */ $userResponse = $resourceOwner->getUserInformation(['oauth_token' => 'token', 'oauth_token_secret' => 'secret']); diff --git a/Tests/OAuth/ResourceOwner/GenericOAuth2ResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/GenericOAuth2ResourceOwnerTest.php index e884074a0..4e054dc85 100644 --- a/Tests/OAuth/ResourceOwner/GenericOAuth2ResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/GenericOAuth2ResourceOwnerTest.php @@ -11,10 +11,8 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; -use Http\Client\Exception\TransferException; use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\GenericOAuth2ResourceOwner; -use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; use HWI\Bundle\OAuthBundle\OAuth\State\State; use HWI\Bundle\OAuthBundle\OAuth\StateInterface; @@ -23,16 +21,11 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\OptionsResolver\Exception\ExceptionInterface; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\Security\Core\Exception\AuthenticationException; class GenericOAuth2ResourceOwnerTest extends ResourceOwnerTestCase { protected $resourceOwnerClass = GenericOAuth2ResourceOwner::class; - /** - * @var GenericOAuth2ResourceOwner - */ - protected $resourceOwner; - protected $resourceOwnerName; - protected $tokenData = ['access_token' => 'token']; protected $options = [ @@ -63,47 +56,49 @@ class GenericOAuth2ResourceOwnerTest extends ResourceOwnerTestCase protected $redirectUrlPart = '&redirect_uri=http%3A%2F%2Fredirect.to%2F'; protected $authorizationUrlParams = []; - protected function setUp(): void - { - $this->resourceOwnerName = str_replace(['generic', 'resourceownertest'], '', strtolower(__CLASS__)); - $this->resourceOwner = $this->createResourceOwner($this->resourceOwnerName); - } - public function testUndefinedOptionThrowsException() { $this->expectException(ExceptionInterface::class); - $this->createResourceOwner($this->resourceOwnerName, ['non_existing' => null]); + $this->createResourceOwner(['non_existing' => null]); } public function testInvalidOptionValueThrowsException() { $this->expectException(ExceptionInterface::class); - $this->createResourceOwner($this->resourceOwnerName, ['csrf' => 'invalid']); + $this->createResourceOwner(['csrf' => 'invalid']); } public function testHandleRequest() { + $resourceOwner = $this->createResourceOwner(); + $request = new Request(['test' => 'test']); - $this->assertFalse($this->resourceOwner->handles($request)); + $this->assertFalse($resourceOwner->handles($request)); $request = new Request(['code' => 'test']); - $this->assertTrue($this->resourceOwner->handles($request)); + $this->assertTrue($resourceOwner->handles($request)); $request = new Request(['code' => 'test', 'test' => 'test']); - $this->assertTrue($this->resourceOwner->handles($request)); + $this->assertTrue($resourceOwner->handles($request)); } public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** @var AbstractUserResponse $userResponse */ - $userResponse = $this->resourceOwner->getUserInformation($this->tokenData); + $userResponse = $resourceOwner->getUserInformation($this->tokenData); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('bar', $userResponse->getNickname()); @@ -114,18 +109,16 @@ public function testGetUserInformation() public function testGetUserInformationFailure() { - $exception = new TransferException(); - - $this->httpClient->expects($this->once()) - ->method('sendRequest') - ->will($this->throwException($exception)); - - try { - $this->resourceOwner->getUserInformation($this->tokenData); - $this->fail('An exception should have been raised'); - } catch (HttpTransportException $e) { - $this->assertSame($exception, $e->getPrevious()); - } + $this->expectException(HttpTransportException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid', null, 401), + ] + ); + $resourceOwner->getUserInformation($this->tokenData); } public function testGetAuthorizationUrl() @@ -136,7 +129,7 @@ public function testGetAuthorizationUrl() $state = new State(['csrf_token' => NonceGenerator::generate()]); } - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, [], [], $state); + $resourceOwner = $this->createResourceOwner([], [], [], $state); if (!$this->csrf) { $this->storage->expects($this->never()) @@ -166,7 +159,7 @@ public function testGetState() $initialState = new State(array_merge($stateParams, ['csrf_token' => NonceGenerator::generate()])); } - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, [], [], $initialState); + $resourceOwner = $this->createResourceOwner([], [], [], $initialState); $this->storage->expects($this->once()) ->method('fetch') ->with($resourceOwner, State::class, 'state') @@ -179,7 +172,7 @@ public function testGetState() public function testGetStateWithoutStoredValues() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, [], [], new State(null)); + $resourceOwner = $this->createResourceOwner([], [], [], new State(null)); $this->storage->expects($this->once()) ->method('fetch') ->with($resourceOwner, State::class, 'state') @@ -197,7 +190,7 @@ public function testGetAuthorizationUrlWithEnabledCsrf() $nonce = NonceGenerator::generate(); $state = new State(['csrf_token' => $nonce]); - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['csrf' => true], [], $state); + $resourceOwner = $this->createResourceOwner(['csrf' => true], [], [], $state); $this->storage->expects($this->once()) ->method('save') @@ -211,125 +204,160 @@ public function testGetAuthorizationUrlWithEnabledCsrf() $this->state = $state->encode(); } - public function testGetAccessToken() - { - $this->mockHttpClient('access_token=code'); - - $request = new Request(['code' => 'somecode']); - - $this->assertEquals( - ['access_token' => 'code'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') - ); - } - - public function testGetAccessTokenJsonResponse() + /** + * @dataProvider provideAccessTokenData + */ + public function testGetAccessToken(string $response, string $contentType) { - $this->mockHttpClient('{"access_token": "code"}', 'application/json'); - - $request = new Request(['code' => 'somecode']); - - $this->assertEquals( - ['access_token' => 'code'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($response, $contentType), + ] ); - } - - public function testGetAccessTokenJsonCharsetResponse() - { - $this->mockHttpClient('{"access_token": "code"}', 'application/json; charset=utf-8'); $request = new Request(['code' => 'somecode']); $this->assertEquals( ['access_token' => 'code'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') + $resourceOwner->getAccessToken($request, 'http://redirect.to/') ); } - public function testGetAccessTokenTextJavascriptResponse() + public function provideAccessTokenData(): iterable { - $this->mockHttpClient('{"access_token": "code"}', 'text/javascript'); - - $request = new Request(['code' => 'somecode']); - - $this->assertEquals( - ['access_token' => 'code'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') - ); - } - - public function testGetAccessTokenTextJavascriptCharsetResponse() - { - $this->mockHttpClient('{"access_token": "code"}', 'text/javascript; charset=utf-8'); - - $request = new Request(['code' => 'somecode']); - - $this->assertEquals( - ['access_token' => 'code'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') - ); + yield 'plain text' => [ + 'access_token=code', + 'text/plain', + ]; + + yield 'json' => [ + '{"access_token": "code"}', + 'application/json', + ]; + + yield 'json with charset' => [ + '{"access_token": "code"}', + 'application/json; charset=utf-8', + ]; + + yield 'javascript' => [ + '{"access_token": "code"}', + 'text/javascript', + ]; + + yield 'javascript with charset' => [ + '{"access_token": "code"}', + 'text/javascript; charset=utf-8', + ]; } public function testGetAccessTokenFailedResponse() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); - $this->mockHttpClient('invalid'); $request = new Request(['code' => 'code']); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid'), + ] + ); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } public function testGetAccessTokenErrorResponse() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); - $this->mockHttpClient('error=foo'); $request = new Request(['code' => 'code']); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('error=foo'), + ] + ); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } - public function testRefreshAccessToken() + /** + * @dataProvider provideRefreshToken + */ + public function testRefreshAccessToken($response, $contentType) { - $this->mockHttpClient('{"access_token": "bar", "expires_in": 3600}', 'application/json'); - $accessToken = $this->resourceOwner->refreshAccessToken('foo'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($response, $contentType), + ] + ); + + $accessToken = $resourceOwner->refreshAccessToken('foo'); $this->assertEquals('bar', $accessToken['access_token']); $this->assertEquals(3600, $accessToken['expires_in']); } - public function testRefreshAccessTokenInvalid() + public function provideRefreshToken(): iterable { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('invalid'); - - $this->resourceOwner->refreshAccessToken('foo'); + yield 'correct token' => [ + '{"access_token": "bar", "expires_in": 3600}', + 'application/json', + ]; } - public function testRefreshAccessTokenError() + /** + * @dataProvider provideInvalidRefreshToken + */ + public function testRefreshAccessTokenInvalid(string $response, string $exceptionClass) { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('{"error": "invalid"}', 'application/json'); + $this->expectException($exceptionClass); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($response), + ] + ); + $resourceOwner->refreshAccessToken('foo'); + } - $this->resourceOwner->refreshAccessToken('foo'); + public function provideInvalidRefreshToken(): iterable + { + yield 'invalid' => [ + 'invalid', + AuthenticationException::class, + ]; + + yield 'invalid json' => [ + '{"error": "invalid"}', + AuthenticationException::class, + ]; } public function testRevokeToken() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); - $this->resourceOwner->revokeToken('token'); + $resourceOwner = $this->createResourceOwner(); + $resourceOwner->revokeToken('token'); } public function testGetSetName() { - $this->assertEquals($this->resourceOwnerName, $this->resourceOwner->getName()); - $this->resourceOwner->setName('foo'); - $this->assertEquals('foo', $this->resourceOwner->getName()); + $resourceOwner = $this->createResourceOwner(); + + $this->assertEquals($this->prepareResourceOwnerName(), $resourceOwner->getName()); + + $resourceOwner->setName('foo'); + $this->assertEquals('foo', $resourceOwner->getName()); } public function testCsrfTokenIsValidWhenDisabled() @@ -338,15 +366,17 @@ public function testCsrfTokenIsValidWhenDisabled() $this->markTestSkipped('CSRF is enabled for this Resource Owner.'); } + $resourceOwner = $this->createResourceOwner(); + $this->storage->expects($this->never()) ->method('fetch'); - $this->assertTrue($this->resourceOwner->isCsrfTokenValid('whatever you want')); + $this->assertTrue($resourceOwner->isCsrfTokenValid('whatever you want')); } public function testCsrfTokenValid() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['csrf' => true]); + $resourceOwner = $this->createResourceOwner(['csrf' => true]); $this->storage->expects($this->once()) ->method('fetch') @@ -358,9 +388,9 @@ public function testCsrfTokenValid() public function testCsrfTokenInvalid() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['csrf' => true]); + $resourceOwner = $this->createResourceOwner(['csrf' => true]); $this->storage->expects($this->once()) ->method('fetch') @@ -372,9 +402,9 @@ public function testCsrfTokenInvalid() public function testCsrfTokenMissing() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['csrf' => true]); + $resourceOwner = $this->createResourceOwner(['csrf' => true]); $resourceOwner->isCsrfTokenValid(null); } @@ -382,11 +412,15 @@ public function testCsrfTokenMissing() public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['user_response_class' => $class]); - $this->mockHttpClient(); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); - /** @var CustomUserResponse */ $userResponse = $resourceOwner->getUserInformation($this->tokenData); $this->assertInstanceOf($class, $userResponse); @@ -397,33 +431,26 @@ public function testCustomResponseClass() $this->assertNull($userResponse->getExpiresIn()); } - protected function getExpectedAuthorizationUrlWithState($stateParameter) - { - // urlencode state parameter since AbstractResourceOwner::normalizeUrl() http_build_query method encodes them again - return $this->authorizationUrlBasePart.'&state='.urlencode($stateParameter).$this->redirectUrlPart; - } - - /** - * @param StateInterface $state Optional - * - * @throws \ReflectionException - * - * @return ResourceOwnerInterface - */ - protected function createResourceOwner(string $name, array $options = [], array $paths = [], ?StateInterface $state = null) - { - $resourceOwner = parent::createResourceOwner($name, $options, $paths); + protected function createResourceOwner( + array $options = [], + array $paths = [], + array $responses = [], + ?StateInterface $state = null + ): GenericOAuth2ResourceOwner { + /** @var GenericOAuth2ResourceOwner $resourceOwner */ + $resourceOwner = parent::createResourceOwner($options, $paths, $responses); $reflection = new \ReflectionClass(\get_class($resourceOwner)); $stateProperty = $reflection->getProperty('state'); $stateProperty->setAccessible(true); - - $stateProperty->setValue($resourceOwner, $state); - - if (null === $state) { - $stateProperty->setValue($resourceOwner, new State($this->state)); - } + $stateProperty->setValue($resourceOwner, $state ?: new State($this->state)); return $resourceOwner; } + + private function getExpectedAuthorizationUrlWithState($stateParameter): string + { + // urlencode state parameter since AbstractResourceOwner::normalizeUrl() http_build_query method encodes them again + return $this->authorizationUrlBasePart.'&state='.urlencode($stateParameter).$this->redirectUrlPart; + } } diff --git a/Tests/OAuth/ResourceOwner/GitHubResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/GitHubResourceOwnerTest.php index e21a0f3ca..e65eb6706 100644 --- a/Tests/OAuth/ResourceOwner/GitHubResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/GitHubResourceOwnerTest.php @@ -12,6 +12,8 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\GitHubResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; +use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; class GitHubResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { @@ -35,35 +37,72 @@ class GitHubResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testRevokeToken() { - $this->httpResponseHttpCode = 204; - $this->mockHttpClient(null, 'application/json'); - - $this->assertTrue($this->resourceOwner->revokeToken('token')); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json', 204), + ] + ); + + $this->assertTrue($resourceOwner->revokeToken('token')); } public function testRevokeTokenFails() { - $this->httpResponseHttpCode = 404; - $this->mockHttpClient('{"id": "666"}', 'application/json'); - - $this->assertFalse($this->resourceOwner->revokeToken('token')); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"id": "666"}', 'application/json', 404), + ] + ); + + $this->assertFalse($resourceOwner->revokeToken('token')); } public function testCustomResponseClass() { - $this->httpClientCalls = 2; - - parent::testCustomResponseClass(); - - $this->httpClientCalls = 1; + $class = CustomUserResponse::class; + + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + $this->createMockResponse($this->userResponse), + ] + ); + + /** @var CustomUserResponse */ + $userResponse = $resourceOwner->getUserInformation($this->tokenData); + + $this->assertInstanceOf($class, $userResponse); + $this->assertEquals('foo666', $userResponse->getUsername()); + $this->assertEquals('foo', $userResponse->getNickname()); + $this->assertEquals('token', $userResponse->getAccessToken()); + $this->assertNull($userResponse->getRefreshToken()); + $this->assertNull($userResponse->getExpiresIn()); } public function testGetUserInformation() { - $this->httpClientCalls = 2; - - parent::testGetUserInformation(); - - $this->httpClientCalls = 1; + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + $this->createMockResponse($this->userResponse), + ] + ); + + /** @var AbstractUserResponse */ + $userResponse = $resourceOwner->getUserInformation($this->tokenData); + + $this->assertEquals('1', $userResponse->getUsername()); + $this->assertEquals('bar', $userResponse->getNickname()); + $this->assertEquals('token', $userResponse->getAccessToken()); + $this->assertNull($userResponse->getRefreshToken()); + $this->assertNull($userResponse->getExpiresIn()); } } diff --git a/Tests/OAuth/ResourceOwner/GitLabResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/GitLabResourceOwnerTest.php index a779bf513..96c2cce2c 100644 --- a/Tests/OAuth/ResourceOwner/GitLabResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/GitLabResourceOwnerTest.php @@ -22,9 +22,14 @@ class GitLabResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testRevokeToken() { - $this->httpResponseHttpCode = 200; - $this->mockHttpClient(null, 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json'), + ] + ); - $this->assertTrue($this->resourceOwner->revokeToken('token')); + $this->assertTrue($resourceOwner->revokeToken('token')); } } diff --git a/Tests/OAuth/ResourceOwner/GoogleResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/GoogleResourceOwnerTest.php index b7a2b2b66..ef798499c 100644 --- a/Tests/OAuth/ResourceOwner/GoogleResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/GoogleResourceOwnerTest.php @@ -12,6 +12,8 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\GoogleResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; +use Symfony\Component\OptionsResolver\Exception\ExceptionInterface; use Symfony\Component\Security\Http\HttpUtils; class GoogleResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -39,29 +41,32 @@ class GoogleResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testInvalidAccessTypeOptionValueThrowsException() { - $this->expectException(\Symfony\Component\OptionsResolver\Exception\ExceptionInterface::class); + $this->expectException(ExceptionInterface::class); - $this->createResourceOwner($this->resourceOwnerName, ['access_type' => 'invalid']); + $this->createResourceOwner(['access_type' => 'invalid']); } public function testInvalidApprovalPromptOptionValueThrowsException() { - $this->expectException(\Symfony\Component\OptionsResolver\Exception\ExceptionInterface::class); + $this->expectException(ExceptionInterface::class); - $this->createResourceOwner($this->resourceOwnerName, ['approval_prompt' => 'invalid']); + $this->createResourceOwner(['approval_prompt' => 'invalid']); } public function testGetAuthorizationUrl() { + $resourceOwner = $this->createResourceOwner(); + $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&access_type=offline', - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/') + $resourceOwner->getAuthorizationUrl('http://redirect.to/') ); } public function testRequestVisibleActions() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['request_visible_actions' => 'http://schemas.google.com/AddActivity'] + $resourceOwner = $this->createResourceOwner( + ['request_visible_actions' => 'http://schemas.google.com/AddActivity'] ); $this->assertEquals( @@ -72,7 +77,7 @@ public function testRequestVisibleActions() public function testApprovalPromptForce() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['approval_prompt' => 'force']); + $resourceOwner = $this->createResourceOwner(['approval_prompt' => 'force']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&access_type=offline&approval_prompt=force', @@ -82,13 +87,13 @@ public function testApprovalPromptForce() public function testHdParameter() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['hd' => 'mycollege.edu']); + $resourceOwner = $this->createResourceOwner(['hd' => 'mycollege.edu']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&access_type=offline&hd=mycollege.edu', $resourceOwner->getAuthorizationUrl('http://redirect.to/') ); - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['hd' => 'mycollege.edu, mycollege.org']); + $resourceOwner = $this->createResourceOwner(['hd' => 'mycollege.edu, mycollege.org']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&access_type=offline&hd=mycollege.edu&mycollege.org', $resourceOwner->getAuthorizationUrl('http://redirect.to/') @@ -97,21 +102,31 @@ public function testHdParameter() public function testRevokeToken() { - $this->httpResponseHttpCode = 200; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"access_token": "bar"}', 'application/json'), + ] + ); - $this->assertTrue($this->resourceOwner->revokeToken('token')); + $this->assertTrue($resourceOwner->revokeToken('token')); } public function testRevokeTokenFails() { - $this->httpResponseHttpCode = 401; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"access_token": "bar"}', 'application/json', 401), + ] + ); - $this->assertFalse($this->resourceOwner->revokeToken('token')); + $this->assertFalse($resourceOwner->revokeToken('token')); } - protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $options) + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface { return parent::setUpResourceOwner( $name, @@ -121,7 +136,8 @@ protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $option 'access_type' => 'offline', ], $options - ) + ), + $responses ); } } diff --git a/Tests/OAuth/ResourceOwner/HubicResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/HubicResourceOwnerTest.php index 8b9915bc0..a09d4e8c8 100644 --- a/Tests/OAuth/ResourceOwner/HubicResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/HubicResourceOwnerTest.php @@ -29,12 +29,18 @@ class HubicResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformationFirstAndLastName() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); /** * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('bar', $userResponse->getFirstName()); $this->assertEquals('foo', $userResponse->getLastName()); @@ -42,9 +48,11 @@ public function testGetUserInformationFirstAndLastName() public function testGetAuthorizationUrl() { + $resourceOwner = $this->createResourceOwner(); + $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F', - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/') + $resourceOwner->getAuthorizationUrl('http://redirect.to/') ); } } diff --git a/Tests/OAuth/ResourceOwner/InstagramResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/InstagramResourceOwnerTest.php index a74f9f349..52fda88ad 100644 --- a/Tests/OAuth/ResourceOwner/InstagramResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/InstagramResourceOwnerTest.php @@ -31,7 +31,13 @@ class InstagramResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['user_response_class' => $class]); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /* @var $userResponse CustomUserResponse */ $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); diff --git a/Tests/OAuth/ResourceOwner/ItembaseResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/ItembaseResourceOwnerTest.php index ee3ef0498..849ed5a96 100644 --- a/Tests/OAuth/ResourceOwner/ItembaseResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/ItembaseResourceOwnerTest.php @@ -14,8 +14,6 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\ItembaseResourceOwner; /** - * Class ItembaseResourceOwnerTest. - * * @author Thomas Bretzke */ class ItembaseResourceOwnerTest extends GenericOAuth2ResourceOwnerTest diff --git a/Tests/OAuth/ResourceOwner/JiraResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/JiraResourceOwnerTest.php index 700e521ef..00420f93a 100644 --- a/Tests/OAuth/ResourceOwner/JiraResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/JiraResourceOwnerTest.php @@ -11,10 +11,9 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; -use Http\Discovery\MessageFactoryDiscovery; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\JiraResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; -use Psr\Http\Message\RequestInterface; use Symfony\Component\Security\Http\HttpUtils; class JiraResourceOwnerTest extends GenericOAuth1ResourceOwnerTest @@ -29,23 +28,17 @@ class JiraResourceOwnerTest extends GenericOAuth1ResourceOwnerTest public function testGetUserInformation() { - $this - ->httpClient->expects($this->exactly(2)) - ->method('sendRequest') - ->willReturnCallback(function (RequestInterface $request) { - $request = $request->withAddedHeader('Content-Type', 'application/json; charset=utf-8'); - - return MessageFactoryDiscovery::find() - ->createResponse( - $this->httpResponseHttpCode, - null, - $request->getHeaders(), - $this->userResponse - ); - }); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + $this->createMockResponse($this->userResponse), + ] + ); $accessToken = ['oauth_token' => 'token', 'oauth_token_secret' => 'secret']; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('asm89', $userResponse->getUsername()); $this->assertEquals('asm89', $userResponse->getNickname()); @@ -57,37 +50,14 @@ public function testGetUserInformation() public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['user_response_class' => $class]); - - $this - ->httpClient->expects($this->atLeastOnce()) - ->method('sendRequest') - ->willReturnCallback(function (RequestInterface $request) { - $request = $request->withAddedHeader('Content-Type', 'application/json; charset=utf-8'); - - return MessageFactoryDiscovery::find() - ->createResponse( - $this->httpResponseHttpCode, - null, - $request->getHeaders(), - $this->userResponse - ); - }); - - $this - ->httpClient->expects($this->atLeastOnce()) - ->method('sendRequest') - ->willReturnCallback(function (RequestInterface $request) { - $request = $request->withAddedHeader('Content-Type', 'text/plain'); - - return MessageFactoryDiscovery::find() - ->createResponse( - $this->httpResponseHttpCode, - null, - $request->getHeaders(), - '' - ); - }); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + $this->createMockResponse('', 'text/plain'), + ] + ); /** @var CustomUserResponse $userResponse */ $userResponse = $resourceOwner->getUserInformation(['oauth_token' => 'token', 'oauth_token_secret' => 'secret']); @@ -97,7 +67,7 @@ public function testCustomResponseClass() $this->assertEquals('foo', $userResponse->getNickname()); } - protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $options) + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface { return parent::setUpResourceOwner( $name, @@ -111,7 +81,8 @@ protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $option 'signature_method' => 'PLAINTEXT', ], $options - ) + ), + $responses ); } } diff --git a/Tests/OAuth/ResourceOwner/LinkedinResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/LinkedinResourceOwnerTest.php index 66e382232..7c51f6c20 100644 --- a/Tests/OAuth/ResourceOwner/LinkedinResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/LinkedinResourceOwnerTest.php @@ -13,6 +13,7 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\LinkedinResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; +use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; class LinkedinResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { @@ -57,21 +58,41 @@ class LinkedinResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testCustomResponseClass() { - $this->httpClientCalls = 2; + $class = CustomUserResponse::class; - parent::testCustomResponseClass(); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); - $this->httpClientCalls = 1; + /** @var CustomUserResponse */ + $userResponse = $resourceOwner->getUserInformation($this->tokenData); + + $this->assertInstanceOf($class, $userResponse); + $this->assertEquals('foo666', $userResponse->getUsername()); + $this->assertEquals('foo', $userResponse->getNickname()); + $this->assertEquals('token', $userResponse->getAccessToken()); + $this->assertNull($userResponse->getRefreshToken()); + $this->assertNull($userResponse->getExpiresIn()); } public function testGetUserInformation() { - $this->httpClientCalls = 2; - - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); /** @var AbstractUserResponse $userResponse */ - $userResponse = $this->resourceOwner->getUserInformation($this->tokenData); + $userResponse = $resourceOwner->getUserInformation($this->tokenData); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('example@website.com', $userResponse->getNickname()); @@ -80,7 +101,5 @@ public function testGetUserInformation() $this->assertEquals('token', $userResponse->getAccessToken()); $this->assertNull($userResponse->getRefreshToken()); $this->assertNull($userResponse->getExpiresIn()); - - $this->httpClientCalls = 1; } } diff --git a/Tests/OAuth/ResourceOwner/OdnoklassnikiResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/OdnoklassnikiResourceOwnerTest.php index 1f0129c51..78b5974cd 100644 --- a/Tests/OAuth/ResourceOwner/OdnoklassnikiResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/OdnoklassnikiResourceOwnerTest.php @@ -12,6 +12,7 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\OdnoklassnikiResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; use Symfony\Component\Security\Http\HttpUtils; class OdnoklassnikiResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -33,7 +34,7 @@ class OdnoklassnikiResourceOwnerTest extends GenericOAuth2ResourceOwnerTest 'lastname' => 'last_name', ]; - protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $options) + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface { return parent::setUpResourceOwner( $name, @@ -43,7 +44,8 @@ protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $option 'application_key' => '123456', ], $options - ) + ), + $responses ); } } diff --git a/Tests/OAuth/ResourceOwner/PaypalResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/PaypalResourceOwnerTest.php index ff17a0cef..c7e3f0a65 100644 --- a/Tests/OAuth/ResourceOwner/PaypalResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/PaypalResourceOwnerTest.php @@ -14,8 +14,6 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\PaypalResourceOwner; /** - * Class PaypalResourceOwnerTest. - * * @author Berny Cantos */ class PaypalResourceOwnerTest extends GenericOAuth2ResourceOwnerTest diff --git a/Tests/OAuth/ResourceOwner/QQResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/QQResourceOwnerTest.php index f61b5f0e2..081f60f6a 100644 --- a/Tests/OAuth/ResourceOwner/QQResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/QQResourceOwnerTest.php @@ -12,8 +12,11 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\QQResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\HttpUtils; class QQResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -39,12 +42,18 @@ class QQResourceOwnerTest extends GenericOAuth2ResourceOwnerTest */ public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token'], ['openid' => '1']); + $userResponse = $resourceOwner->getUserInformation($this->tokenData, ['openid' => '1']); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('bar', $userResponse->getNickname()); @@ -59,12 +68,17 @@ public function testGetUserInformation() public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner('oauth2', ['user_response_class' => $class]); - $this->mockHttpClient('{"ret": 0}'); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse('{"ret": 0}'), + ] + ); /** @var CustomUserResponse $userResponse */ - $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token'], ['openid' => '1']); + $userResponse = $resourceOwner->getUserInformation($this->tokenData, ['openid' => '1']); $this->assertInstanceOf($class, $userResponse); $this->assertEquals('foo666', $userResponse->getUsername()); @@ -74,18 +88,39 @@ public function testCustomResponseClass() $this->assertNull($userResponse->getExpiresIn()); } + public function testGetUserInformationFailure() + { + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid', null, 401), + ] + ); + + $resourceOwner->getUserInformation($this->tokenData, ['openid' => '1']); + } + /** * QQ returns access token in jsonp format. */ public function testGetAccessTokenJsonpResponse() { - $this->mockHttpClient('callback({"access_token": "code"});'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('callback({"access_token": "code"});'), + ] + ); $request = new Request(['code' => 'somecode']); $this->assertEquals( ['access_token' => 'code'], - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/') + $resourceOwner->getAccessToken($request, 'http://redirect.to/') ); } @@ -94,16 +129,22 @@ public function testGetAccessTokenJsonpResponse() */ public function testGetAccessTokenErrorResponse() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); - - $this->mockHttpClient('callback({"error": 1, "msg": "error"})'); + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('callback({"error": 1, "msg": "error"})'), + ] + ); $request = new Request(['code' => 'code']); - $this->resourceOwner->getAccessToken($request, 'http://redirect.to/'); + $resourceOwner->getAccessToken($request, 'http://redirect.to/'); } - protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $options) + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface { return parent::setUpResourceOwner( $name, @@ -116,7 +157,8 @@ protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $option 'me_url' => 'https://graph.qq.com/oauth2.0/me', ], $options - ) + ), + $responses ); } } diff --git a/Tests/OAuth/ResourceOwner/ResourceOwnerTestCase.php b/Tests/OAuth/ResourceOwner/ResourceOwnerTestCase.php index 55b63e577..fd2298521 100644 --- a/Tests/OAuth/ResourceOwner/ResourceOwnerTestCase.php +++ b/Tests/OAuth/ResourceOwner/ResourceOwnerTestCase.php @@ -11,25 +11,19 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; -use Http\Client\Common\HttpMethodsClient; -use Http\Client\HttpClient; -use Http\Discovery\MessageFactoryDiscovery; -use Http\Message\MessageFactory\GuzzleMessageFactory; use HWI\Bundle\OAuthBundle\OAuth\RequestDataStorageInterface; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\AbstractResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Psr\Http\Message\RequestInterface; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\Security\Http\HttpUtils; abstract class ResourceOwnerTestCase extends TestCase { - /** @var MockObject&HttpClient */ + /** @var MockHttpClient */ protected $httpClient; - protected $httpResponse; - protected $httpResponseContentType; - protected $httpResponseHttpCode = 200; - protected $httpClientCalls; /** @var MockObject&RequestDataStorageInterface */ protected $storage; @@ -39,52 +33,55 @@ abstract class ResourceOwnerTestCase extends TestCase protected $options = []; protected $paths = []; + /** @var AbstractResourceOwner */ protected $resourceOwnerClass; - protected function mockHttpClient($response = '', $contentType = 'text/plain') + protected function createMockResponse(?string $response, ?string $contentType = null, ?int $httpCode = null): MockResponse { - if (null !== $this->httpClientCalls) { - $mock = $this->httpClient->expects($this->exactly($this->httpClientCalls)); - } else { - $mock = $this->httpClient->expects($this->once()); - } + return new MockResponse( + $response, + [ + 'http_code' => $httpCode ?: 200, + 'response_headers' => [ + 'Content-Type' => $contentType ?: 'application/json', + ], + ] + ); + } - $mock->method('sendRequest') - ->willReturnCallback(function (RequestInterface $request) use ($response, $contentType) { - $request = $request->withAddedHeader('Content-Type', $contentType ?: $this->httpResponseContentType); - - return MessageFactoryDiscovery::find() - ->createResponse( - $this->httpResponseHttpCode, - null, - $request->getHeaders(), - $response ?: $this->httpResponse - ) - ; - }); + protected function mockHttpClient(string $response = '', string $contentType = 'text/plain') + { + $this->httpClient = new MockHttpClient( + [ + $this->createMockResponse($response, $contentType), + ] + ); } - protected function createResourceOwner(string $name, array $options = [], array $paths = []) + protected function prepareResourceOwnerName(): string { - $this->httpClient = $this->createMock(HttpClient::class); + return str_replace(['generic', 'resourceownertest'], '', strtolower(__CLASS__)); + } + protected function createResourceOwner(array $options = [], array $paths = [], array $responses = []) + { $this->storage = $this->createMock(RequestDataStorageInterface::class); /** @var HttpUtils $httpUtils */ $httpUtils = $this->createMock(HttpUtils::class); - $resourceOwner = $this->setUpResourceOwner($name, $httpUtils, array_merge($this->options, $options)); + $resourceOwner = $this->setUpResourceOwner( + $this->prepareResourceOwnerName(), + $httpUtils, + array_merge($this->options, $options), + $responses + ); $resourceOwner->addPaths(array_merge($this->paths, $paths)); return $resourceOwner; } - /** - * @param string $name - * - * @return \HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface - */ - protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $options) + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface { if (!$this->resourceOwnerClass) { throw new \RuntimeException('Missing resource owner class declaration!'); @@ -94,6 +91,6 @@ protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $option throw new \RuntimeException('Class is not implementing "ResourceOwnerInterface"!'); } - return new $this->resourceOwnerClass(new HttpMethodsClient($this->httpClient, new GuzzleMessageFactory()), $httpUtils, $options, $name, $this->storage); + return new $this->resourceOwnerClass($httpUtils, $options, $name, $this->storage, $this->httpClient ?: new MockHttpClient($responses)); } } diff --git a/Tests/OAuth/ResourceOwner/RunKeeperResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/RunKeeperResourceOwnerTest.php index 587269cbb..9a422e729 100644 --- a/Tests/OAuth/ResourceOwner/RunKeeperResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/RunKeeperResourceOwnerTest.php @@ -14,8 +14,6 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\RunKeeperResourceOwner; /** - * RunKeeperResourceOwnerTest. - * * @author Artem Genvald */ class RunKeeperResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -41,9 +39,15 @@ class RunKeeperResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); - - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); + + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('Foo Bar', $userResponse->getRealName()); $this->assertEquals('http://www.gravatar.com/avatar/default', $userResponse->getProfilePicture()); diff --git a/Tests/OAuth/ResourceOwner/SalesforceResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/SalesforceResourceOwnerTest.php index 7d49b7a34..79cbc4ba1 100644 --- a/Tests/OAuth/ResourceOwner/SalesforceResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/SalesforceResourceOwnerTest.php @@ -11,9 +11,9 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; -use Http\Client\Exception\TransferException; use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\SalesforceResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; class SalesforceResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { @@ -38,12 +38,20 @@ class SalesforceResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token', 'id' => 'someuser']); + $userResponse = $resourceOwner->getUserInformation( + ['access_token' => 'token', 'id' => 'https://login.salesforce.com/services/oauth2/someuser'] + ); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('bar', $userResponse->getNickname()); @@ -55,18 +63,18 @@ public function testGetUserInformation() public function testGetUserInformationFailure() { - $exception = new TransferException(); + $this->expectException(HttpTransportException::class); - $this->httpClient->expects($this->once()) - ->method('sendRequest') - ->will($this->throwException($exception)); - - try { - $this->resourceOwner->getUserInformation(['access_token' => 'token', 'id' => 'someuser']); - $this->fail('An exception should have been raised'); - } catch (HttpTransportException $e) { - $this->assertSame($exception, $e->getPrevious()); - } + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid', 'application/json; charset=utf-8', 401), + ] + ); + $resourceOwner->getUserInformation( + ['access_token' => 'token', 'id' => 'https://login.salesforce.com/services/oauth2/someuser'] + ); } public function testCustomResponseClass() diff --git a/Tests/OAuth/ResourceOwner/SensioConnectResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/SensioConnectResourceOwnerTest.php index dd230952a..7390e63a9 100644 --- a/Tests/OAuth/ResourceOwner/SensioConnectResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/SensioConnectResourceOwnerTest.php @@ -13,6 +13,7 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\SensioConnectResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\Response\SensioConnectUserResponse; +use Symfony\Component\Security\Core\Exception\AuthenticationException; class SensioConnectResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { @@ -30,9 +31,13 @@ protected function setUp(): void public function testGetUserInformation() { $class = SensioConnectUserResponse::class; - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['user_response_class' => $class]); - - $this->mockHttpClient($this->userResponse); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse, 'application/xml'), + ] + ); /** * @var SensioConnectUserResponse @@ -46,4 +51,23 @@ public function testGetUserInformation() $this->assertEquals('fake@email.com', $userResponse->getEmail()); $this->assertEquals('token', $userResponse->getAccessToken()); } + + public function testCustomResponseClass() + { + $this->markTestSkipped('SensioConnect already tests this approach'); + } + + public function testGetUserInformationFailure() + { + $this->expectException(AuthenticationException::class); + + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid', 'application/xml'), + ] + ); + $resourceOwner->getUserInformation(['access_token' => 'token']); + } } diff --git a/Tests/OAuth/ResourceOwner/SinaWeiboResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/SinaWeiboResourceOwnerTest.php index f130a39ee..7460af11a 100644 --- a/Tests/OAuth/ResourceOwner/SinaWeiboResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/SinaWeiboResourceOwnerTest.php @@ -11,9 +11,9 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; -use Http\Client\Exception\TransferException; use HWI\Bundle\OAuthBundle\OAuth\Exception\HttpTransportException; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\SinaWeiboResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; class SinaWeiboResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -35,12 +35,18 @@ class SinaWeiboResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token', 'uid' => '1']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token', 'uid' => '1']); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('bar', $userResponse->getNickname()); @@ -51,29 +57,32 @@ public function testGetUserInformation() public function testGetUserInformationFailure() { - $exception = new TransferException(); - - $this->httpClient->expects($this->once()) - ->method('sendRequest') - ->will($this->throwException($exception)); + $this->expectException(HttpTransportException::class); - try { - $this->resourceOwner->getUserInformation(['access_token' => 'token', 'uid' => 'someuser']); - $this->fail('An exception should have been raised'); - } catch (HttpTransportException $e) { - $this->assertSame($exception, $e->getPrevious()); - } + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('invalid'), + ] + ); + $resourceOwner->getUserInformation(['access_token' => 'token', 'uid' => 'someuser']); } public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner('oauth2', ['user_response_class' => $class]); - $this->mockHttpClient(); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse + * @var CustomUserResponse */ $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token', 'uid' => '1']); diff --git a/Tests/OAuth/ResourceOwner/SpotifyResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/SpotifyResourceOwnerTest.php index f10d8091e..4b316f56b 100644 --- a/Tests/OAuth/ResourceOwner/SpotifyResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/SpotifyResourceOwnerTest.php @@ -12,6 +12,7 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\SpotifyResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse; /** @@ -56,12 +57,18 @@ class SpotifyResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('wizzler', $userResponse->getUsername()); $this->assertEquals('wizzler', $userResponse->getNickname()); @@ -73,9 +80,13 @@ public function testGetUserInformation() public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner('oauth2', ['user_response_class' => $class]); - - $this->mockHttpClient(); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** * @var CustomUserResponse diff --git a/Tests/OAuth/ResourceOwner/StackExchangeResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/StackExchangeResourceOwnerTest.php index df2c73f46..3db801548 100644 --- a/Tests/OAuth/ResourceOwner/StackExchangeResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/StackExchangeResourceOwnerTest.php @@ -12,6 +12,7 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\StackExchangeResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; use Symfony\Component\Security\Http\HttpUtils; class StackExchangeResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -38,12 +39,13 @@ class StackExchangeResourceOwnerTest extends GenericOAuth2ResourceOwnerTest protected $authorizationUrlBasePart = 'http://user.auth/?test=2&response_type=code&client_id=clientid&scope=no_expiry'; - protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $options) + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface { return parent::setUpResourceOwner( $name, $httpUtils, - array_merge($options, ['key' => 'baz']) + array_merge($options, ['key' => 'baz']), + $responses ); } } diff --git a/Tests/OAuth/ResourceOwner/StereomoodResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/StereomoodResourceOwnerTest.php index 0ffb40679..10685e473 100644 --- a/Tests/OAuth/ResourceOwner/StereomoodResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/StereomoodResourceOwnerTest.php @@ -34,7 +34,14 @@ public function testGetUserInformation() 'oauth_token_secret' => 'secret', ]; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('token', $userResponse->getUsername()); $this->assertEquals('token', $userResponse->getNickname()); @@ -47,7 +54,7 @@ public function testGetUserInformation() public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner('oauth1', ['user_response_class' => $class]); + $resourceOwner = $this->createResourceOwner(['user_response_class' => $class]); $accessToken = [ 'oauth_token' => 'token', diff --git a/Tests/OAuth/ResourceOwner/StravaResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/StravaResourceOwnerTest.php index b475ffb9b..b94576240 100644 --- a/Tests/OAuth/ResourceOwner/StravaResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/StravaResourceOwnerTest.php @@ -14,8 +14,6 @@ use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\StravaResourceOwner; /** - * StravaResourceOwnerTest. - * * @author Artem Genvald */ class StravaResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -46,9 +44,15 @@ class StravaResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('Foo Bar', $userResponse->getRealName()); diff --git a/Tests/OAuth/ResourceOwner/ToshlResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/ToshlResourceOwnerTest.php index 6663dc84e..c0d5b2661 100644 --- a/Tests/OAuth/ResourceOwner/ToshlResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/ToshlResourceOwnerTest.php @@ -12,6 +12,7 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\ToshlResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse; class ToshlResourceOwnerTest extends GenericOAuth2ResourceOwnerTest { @@ -37,28 +38,44 @@ class ToshlResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testRevokeToken() { - $this->httpResponseHttpCode = 204; - $this->mockHttpClient(null, 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse(null, 'application/json', 204), + ] + ); - $this->assertTrue($this->resourceOwner->revokeToken('token')); + $this->assertTrue($resourceOwner->revokeToken('token')); } public function testRevokeTokenFails() { - $this->httpResponseHttpCode = 404; - $this->mockHttpClient('{"id": "666"}', 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"id": "666"}', 'application/json', 404), + ] + ); - $this->assertFalse($this->resourceOwner->revokeToken('token')); + $this->assertFalse($resourceOwner->revokeToken('token')); } public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); /** - * @var \HWI\Bundle\OAuthBundle\OAuth\Response\AbstractUserResponse + * @var AbstractUserResponse */ - $userResponse = $this->resourceOwner->getUserInformation(['access_token' => 'token']); + $userResponse = $resourceOwner->getUserInformation(['access_token' => 'token']); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('example@website.com', $userResponse->getNickname()); diff --git a/Tests/OAuth/ResourceOwner/TraktResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/TraktResourceOwnerTest.php index cbfe56930..85d544f14 100644 --- a/Tests/OAuth/ResourceOwner/TraktResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/TraktResourceOwnerTest.php @@ -39,10 +39,16 @@ class TraktResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); $accessToken = ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'access_token' => 'token']; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('georges', $userResponse->getUsername()); $this->assertEquals('georges', $userResponse->getNickname()); diff --git a/Tests/OAuth/ResourceOwner/TrelloResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/TrelloResourceOwnerTest.php index 49d49e4f4..b5520123b 100644 --- a/Tests/OAuth/ResourceOwner/TrelloResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/TrelloResourceOwnerTest.php @@ -33,15 +33,21 @@ class TrelloResourceOwnerTest extends GenericOAuth1ResourceOwnerTest public function testGetAuthorizationUrlContainOAuthTokenAndSecret() { - $this->mockHttpClient('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"oauth_token": "token", "oauth_token_secret": "secret"}', 'application/json; charset=utf-8'), + ] + ); $this->storage->expects($this->once()) ->method('save') - ->with($this->resourceOwner, ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'timestamp' => time()]); + ->with($resourceOwner, ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'timestamp' => time()]); $this->assertEquals( 'http://user.auth/?test=3&scope=read&oauth_token=token', - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/') + $resourceOwner->getAuthorizationUrl('http://redirect.to/') ); } } diff --git a/Tests/OAuth/ResourceOwner/TwitterResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/TwitterResourceOwnerTest.php index 3c904d8a9..fd558dc15 100644 --- a/Tests/OAuth/ResourceOwner/TwitterResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/TwitterResourceOwnerTest.php @@ -30,7 +30,14 @@ class TwitterResourceOwnerTest extends GenericOAuth1ResourceOwnerTest public function testGetUserInformationWithEmail() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['include_email' => true]); + $resourceOwner = $this->createResourceOwner( + ['include_email' => true], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); + $accessToken = ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'user_id' => '1', 'screen_name' => 'bar']; $resourceOwner->getUserInformation($accessToken); @@ -40,10 +47,16 @@ public function testGetUserInformationWithEmail() public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); $accessToken = ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'user_id' => '1', 'screen_name' => 'bar']; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('bar', $userResponse->getNickname()); diff --git a/Tests/OAuth/ResourceOwner/XingResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/XingResourceOwnerTest.php index fe023935e..d9d188971 100644 --- a/Tests/OAuth/ResourceOwner/XingResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/XingResourceOwnerTest.php @@ -44,10 +44,16 @@ class XingResourceOwnerTest extends GenericOAuth1ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); $accessToken = ['oauth_token' => 'token', 'oauth_token_secret' => 'secret']; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('42', $userResponse->getUsername()); $this->assertEquals('foo bar', $userResponse->getNickname()); diff --git a/Tests/OAuth/ResourceOwner/YahooResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/YahooResourceOwnerTest.php index 34be83b99..99387ba01 100644 --- a/Tests/OAuth/ResourceOwner/YahooResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/YahooResourceOwnerTest.php @@ -33,10 +33,16 @@ class YahooResourceOwnerTest extends GenericOAuth1ResourceOwnerTest public function testGetUserInformation() { - $this->mockHttpClient($this->userResponse, 'application/json; charset=utf-8'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse($this->userResponse, 'application/json; charset=utf-8'), + ] + ); $accessToken = ['oauth_token' => 'token', 'oauth_token_secret' => 'secret', 'xoauth_yahoo_guid' => 1]; - $userResponse = $this->resourceOwner->getUserInformation($accessToken); + $userResponse = $resourceOwner->getUserInformation($accessToken); $this->assertEquals('1', $userResponse->getUsername()); $this->assertEquals('bar', $userResponse->getNickname()); @@ -48,9 +54,13 @@ public function testGetUserInformation() public function testCustomResponseClass() { $class = CustomUserResponse::class; - $resourceOwner = $this->createResourceOwner('oauth1', ['user_response_class' => $class]); - - $this->mockHttpClient(); + $resourceOwner = $this->createResourceOwner( + ['user_response_class' => $class], + [], + [ + $this->createMockResponse($this->userResponse), + ] + ); /** * @var CustomUserResponse diff --git a/Tests/OAuth/ResourceOwner/YoutubeResourceOwnerTest.php b/Tests/OAuth/ResourceOwner/YoutubeResourceOwnerTest.php index e4b55fdb0..ab0957399 100644 --- a/Tests/OAuth/ResourceOwner/YoutubeResourceOwnerTest.php +++ b/Tests/OAuth/ResourceOwner/YoutubeResourceOwnerTest.php @@ -12,6 +12,8 @@ namespace HWI\Bundle\OAuthBundle\Tests\OAuth\ResourceOwner; use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\YoutubeResourceOwner; +use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; use Symfony\Component\Security\Http\HttpUtils; class YoutubeResourceOwnerTest extends GenericOAuth2ResourceOwnerTest @@ -37,29 +39,32 @@ class YoutubeResourceOwnerTest extends GenericOAuth2ResourceOwnerTest public function testInvalidAccessTypeOptionValueThrowsException() { - $this->expectException(\Symfony\Component\OptionsResolver\Exception\InvalidOptionsException::class); + $this->expectException(InvalidOptionsException::class); - $this->createResourceOwner($this->resourceOwnerName, ['access_type' => 'invalid']); + $this->createResourceOwner(['access_type' => 'invalid']); } public function testInvalidApprovalPromptOptionValueThrowsException() { - $this->expectException(\Symfony\Component\OptionsResolver\Exception\InvalidOptionsException::class); + $this->expectException(InvalidOptionsException::class); - $this->createResourceOwner($this->resourceOwnerName, ['approval_prompt' => 'invalid']); + $this->createResourceOwner(['approval_prompt' => 'invalid']); } public function testGetAuthorizationUrl() { + $resourceOwner = $this->createResourceOwner(); + $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&access_type=offline', - $this->resourceOwner->getAuthorizationUrl('http://redirect.to/') + $resourceOwner->getAuthorizationUrl('http://redirect.to/') ); } public function testRequestVisibleActions() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['request_visible_actions' => 'http://schemas.google.com/AddActivity'] + $resourceOwner = $this->createResourceOwner( + ['request_visible_actions' => 'http://schemas.google.com/AddActivity'] ); $this->assertEquals( @@ -70,7 +75,7 @@ public function testRequestVisibleActions() public function testApprovalPromptForce() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['approval_prompt' => 'force']); + $resourceOwner = $this->createResourceOwner(['approval_prompt' => 'force']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&access_type=offline&approval_prompt=force', @@ -80,7 +85,7 @@ public function testApprovalPromptForce() public function testHdParameter() { - $resourceOwner = $this->createResourceOwner($this->resourceOwnerName, ['hd' => 'mycollege.edu']); + $resourceOwner = $this->createResourceOwner(['hd' => 'mycollege.edu']); $this->assertEquals( $this->options['authorization_url'].'&response_type=code&client_id=clientid&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&state=eyJzdGF0ZSI6InJhbmRvbSJ9&redirect_uri=http%3A%2F%2Fredirect.to%2F&access_type=offline&hd=mycollege.edu', $resourceOwner->getAuthorizationUrl('http://redirect.to/') @@ -89,21 +94,31 @@ public function testHdParameter() public function testRevokeToken() { - $this->httpResponseHttpCode = 200; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"access_token": "bar"}', 'application/json'), + ] + ); - $this->assertTrue($this->resourceOwner->revokeToken('token')); + $this->assertTrue($resourceOwner->revokeToken('token')); } public function testRevokeTokenFails() { - $this->httpResponseHttpCode = 401; - $this->mockHttpClient('{"access_token": "bar"}', 'application/json'); + $resourceOwner = $this->createResourceOwner( + [], + [], + [ + $this->createMockResponse('{"access_token": "bar"}', 'application/json', 401), + ] + ); - $this->assertFalse($this->resourceOwner->revokeToken('token')); + $this->assertFalse($resourceOwner->revokeToken('token')); } - protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $options) + protected function setUpResourceOwner(string $name, HttpUtils $httpUtils, array $options, array $responses): ResourceOwnerInterface { return parent::setUpResourceOwner( $name, @@ -113,7 +128,8 @@ protected function setUpResourceOwner($name, HttpUtils $httpUtils, array $option 'access_type' => 'offline', ], $options - ) + ), + $responses ); } } diff --git a/composer.json b/composer.json index a662fdc07..de9eeae18 100644 --- a/composer.json +++ b/composer.json @@ -100,13 +100,7 @@ "symfony/form": "^4.4|^5.1", "symfony/yaml": "^4.4|^5.1", "symfony/templating": "^4.4|^5.1", - - "psr/http-message": "^1.0", - "php-http/client-implementation": "^1.0", - "php-http/httplug": "^2.0", - "php-http/client-common": "^2.0", - "php-http/message-factory": "^1.0", - "php-http/discovery": "^1.0" + "symfony/http-client": "^5.3" }, "require-dev": { @@ -119,8 +113,6 @@ "symfony/twig-bundle": "^4.4|^5.1", "symfony/stopwatch": "^5.1", "symfony/translation": "^4.4|^5.1", - "php-http/httplug-bundle": "^1.7", - "php-http/guzzle6-adapter": "^2.0", "phpunit/phpunit": "^9.5", "friendsofphp/php-cs-fixer": "^3.0", "symfony/monolog-bundle": "^3.4", @@ -142,7 +134,6 @@ "suggest": { "doctrine/doctrine-bundle": "to use Doctrine user provider", - "php-http/httplug-bundle": "to provide required HTTP client with ease.", "symfony/property-access": "to use FOSUB integration with this bundle", "symfony/twig-bundle": "to use the Twig hwi_oauth_* functions" },