diff --git a/.gitattributes b/.gitattributes index 2c05229..e01c8d8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,4 +5,4 @@ /tests/ export-ignore /.gitignore export-ignore /phpunit.xml.dist export-ignore -/README.md export-ignore \ No newline at end of file +#/README.md export-ignore \ No newline at end of file diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 37d7fed..8e036e5 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -2,20 +2,31 @@ name: PHP tests on: [push, pull_request] jobs: php-linter: - name: PHP Syntax check 7.2|7.3|8.0|8.1 + name: PHP Syntax check 5.6|7.2|8.0|8.1 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 + - name: PHP syntax checker 5.6 + uses: prestashop/github-action-php-lint/5.6@master + with: + folder-to-exclude: "! -path \"./vendor/*\" ! -path \"./tests/*\"" + - name: PHP syntax checker 7.2 uses: prestashop/github-action-php-lint/7.2@master + with: + folder-to-exclude: "! -path \"./vendor/*\" ! -path \"./tests/*\"" - name: PHP syntax checker 8.0 uses: prestashop/github-action-php-lint/8.0@master + with: + folder-to-exclude: "! -path \"./vendor/*\" ! -path \"./tests/*\"" - name: PHP syntax checker 8.1 uses: prestashop/github-action-php-lint/8.1@master + with: + folder-to-exclude: "! -path \"./vendor/*\" ! -path \"./tests/*\"" php-cs-fixer: name: PHP-CS-Fixer diff --git a/README.md b/README.md index ecc3789..7fc7256 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ This package provides PrestaShop OAuth 2.0 support for the PHP League's [OAuth 2.0 Client](https://github.com/thephpleague/oauth2-client). +[![Source Code](https://img.shields.io/badge/source-PrestaShopCorp/oauth2--prestashop-blue.svg?style=flat-square)](https://github.com/PrestaShopCorp/oauth2-prestashop) +[![Latest Version](https://img.shields.io/github/release/PrestaShopCorp/oauth2-prestashop.svg?style=flat-square)](https://github.com/PrestaShopCorp/oauth2-prestashop/releases) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/PrestaShopCorp/oauth2-prestashop/blob/main/LICENSE) +[![Build Status](https://img.shields.io/github/actions/workflow/status/PrestaShopCorp/oauth2-prestashop/.github/workflows/php.yml?label=CI&logo=github&style=flat-square)](https://github.com/PrestaShopCorp/oauth2-prestashop/actions?query=workflow%3ACI) +[![Total Downloads](https://img.shields.io/packagist/dt/PrestaShopCorp/oauth2-prestashop.svg?style=flat-square)](https://packagist.org/packages/prestashopcorp/oauth2-prestashop) + +--- + ## Installation ``` diff --git a/composer.json b/composer.json index 9a38908..88250ad 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "authentication" ], "require": { - "php": ">=7.1", + "php": ">=5.6", "league/oauth2-client": "^2.0" }, "require-dev": { diff --git a/src/Provider/LogoutTrait.php b/src/Provider/LogoutTrait.php index 06f756d..0622716 100644 --- a/src/Provider/LogoutTrait.php +++ b/src/Provider/LogoutTrait.php @@ -12,9 +12,9 @@ trait LogoutTrait /** * @return string */ - public function getBaseSessionLogoutUrl(): string + public function getBaseSessionLogoutUrl() { - return 'https://oauth.prestashop.com/oauth2/sessions/logout'; + return $this->getWellKnown()->end_session_endpoint; } /** @@ -26,7 +26,7 @@ public function getBaseSessionLogoutUrl(): string * * @throws \Exception */ - public function getLogoutUrl(array $options = []): string + public function getLogoutUrl(array $options = []) { $base = $this->getBaseSessionLogoutUrl(); $params = $this->getLogoutParameters($options); @@ -42,7 +42,7 @@ public function getLogoutUrl(array $options = []): string * * @throws \Exception */ - protected function getLogoutParameters(array $options): array + protected function getLogoutParameters(array $options) { if (empty($options['id_token_hint'])) { // $options['id_token_hint'] = $this->getSessionAccessToken()->getValues()['id_token']; @@ -67,7 +67,7 @@ protected function getLogoutParameters(array $options): array * * @return string Query string */ - protected function getLogoutQuery(array $params): string + protected function getLogoutQuery(array $params) { return $this->buildQueryString($params); } diff --git a/src/Provider/PrestaShop.php b/src/Provider/PrestaShop.php index bd9ee2d..bf0b9ed 100644 --- a/src/Provider/PrestaShop.php +++ b/src/Provider/PrestaShop.php @@ -52,12 +52,83 @@ class PrestaShop extends AbstractProvider */ protected $uiLocales; + /** + * @var WellKnown + */ + protected $wellKnown; + + /** + * @var bool + */ + protected $verify = true; + + /** + * @param array $options + * @param array $collaborators + * + * @throws \Exception + */ + public function __construct(array $options = [], array $collaborators = []) + { + parent::__construct($options, $collaborators); + } + + /** + * @return string + */ + public function getOauth2Url() + { + return 'https://oauth.prestashop.com'; + } + + /** + * @return WellKnown + */ + public function getWellKnown() + { + /* @phpstan-ignore-next-line */ + if (!isset($this->wellKnown)) { + try { + $this->wellKnown = new WellKnown( + $this->fetchWellKnown($this->getOauth2Url(), $this->verify) + ); + } catch (\Error $e) { + } catch (\Exception $e) { + } + if (isset($e)) { + $this->wellKnown = new WellKnown(); + } + } + + return $this->wellKnown; + } + + /** + * @param string $url + * @param bool $secure + * + * @return array + * + * @throws \Exception + */ + protected function fetchWellKnown($url, $secure = true) + { + $wellKnownUrl = $url; + if (strpos($wellKnownUrl, '/.well-known') === false) { + $wellKnownUrl = preg_replace('/\/?$/', '/.well-known/openid-configuration', $wellKnownUrl); + } + + $response = $this->getResponse($this->getRequest('GET', $wellKnownUrl)); + + return json_decode($response->getBody(), true); + } + /** * @return string */ - public function getBaseAuthorizationUrl(): string + public function getBaseAuthorizationUrl() { - return 'https://oauth.prestashop.com/oauth2/auth'; + return $this->getWellKnown()->authorization_endpoint; } /** @@ -65,9 +136,9 @@ public function getBaseAuthorizationUrl(): string * * @return string */ - public function getBaseAccessTokenUrl(array $params): string + public function getBaseAccessTokenUrl(array $params) { - return 'https://oauth.prestashop.com/oauth2/token'; + return $this->getWellKnown()->token_endpoint; } /** @@ -75,9 +146,9 @@ public function getBaseAccessTokenUrl(array $params): string * * @return string */ - public function getResourceOwnerDetailsUrl(AccessToken $token): string + public function getResourceOwnerDetailsUrl(AccessToken $token) { - return 'https://oauth.prestashop.com/userinfo'; + return $this->getWellKnown()->userinfo_endpoint; } /** @@ -85,7 +156,7 @@ public function getResourceOwnerDetailsUrl(AccessToken $token): string * * @return string[] */ - protected function getAuthorizationParameters(array $options): array + protected function getAuthorizationParameters(array $options) { if (empty($options['prompt']) && $this->prompt) { $options['prompt'] = $this->prompt; @@ -107,7 +178,7 @@ protected function getAuthorizationParameters(array $options): array /** * @return string[] */ - public function getDefaultScopes(): array + public function getDefaultScopes() { return ['openid', 'offline_access']; } @@ -115,7 +186,7 @@ public function getDefaultScopes(): array /** * @return string */ - protected function getScopeSeparator(): string + protected function getScopeSeparator() { return ' '; } @@ -128,13 +199,13 @@ protected function getScopeSeparator(): string * * @throws IdentityProviderException */ - protected function checkResponse(ResponseInterface $response, $data): void + protected function checkResponse(ResponseInterface $response, $data) { if ($response->getStatusCode() !== 200) { $errorDescription = ''; $error = ''; if (\is_array($data) && !empty($data)) { - $errorDescription = $data['error_description'] ?? $data['message']; + $errorDescription = isset($data['error_description']) ? $data['error_description'] : $data['message']; $error = $data['error']; } throw new IdentityProviderException(sprintf('%d - %s: %s', $response->getStatusCode(), $error, $errorDescription), $response->getStatusCode(), $data); @@ -147,7 +218,7 @@ protected function checkResponse(ResponseInterface $response, $data): void * * @return PrestaShopUser */ - protected function createResourceOwner(array $response, AccessToken $token): PrestaShopUser + protected function createResourceOwner(array $response, AccessToken $token) { return new PrestaShopUser($response); } @@ -159,7 +230,7 @@ protected function createResourceOwner(array $response, AccessToken $token): Pre * * @return PrestaShopUser */ - public function getResourceOwner(AccessToken $token): PrestaShopUser + public function getResourceOwner(AccessToken $token) { /** @var PrestaShopUser $resourceOwner */ $resourceOwner = parent::getResourceOwner($token); diff --git a/src/Provider/WellKnown.php b/src/Provider/WellKnown.php new file mode 100644 index 0000000..d7817ad --- /dev/null +++ b/src/Provider/WellKnown.php @@ -0,0 +1,144 @@ +init($infos); + } + + /** + * @param array $infos + * + * @return void + */ + public function init($infos) + { + foreach ($infos as $key => $value) { + if (property_exists($this, $key)) { + $this->$key = $value; + } + } + } +} diff --git a/tests/src/Provider/LogoutTraitTest.php b/tests/src/Provider/LogoutTraitTest.php index c5a0d44..5d5d5fa 100644 --- a/tests/src/Provider/LogoutTraitTest.php +++ b/tests/src/Provider/LogoutTraitTest.php @@ -12,6 +12,9 @@ class LogoutTraitTest extends TestCase */ private $provider; + /** + * @return void + */ protected function setUp(): void { $this->provider = new PrestaShop([ @@ -27,7 +30,7 @@ protected function setUp(): void /** * @test */ - public function itShouldGenerateLogoutUrl(): void + public function itShouldGenerateLogoutUrl() { $idToken = 'someRandomIdToken'; @@ -49,7 +52,7 @@ public function itShouldGenerateLogoutUrl(): void /** * @test */ - public function itShouldGenerateLogoutUrlWithOptionalParameters(): void + public function itShouldGenerateLogoutUrlWithOptionalParameters() { $idToken = 'someRandomIdToken'; $postLogoutRedirectUri = 'https://overriden-post-logout-uri.net'; @@ -73,7 +76,7 @@ public function itShouldGenerateLogoutUrlWithOptionalParameters(): void /** * @test */ - public function itShouldGenerateLogoutUrlWithOptionalOnlyParameters(): void + public function itShouldGenerateLogoutUrlWithOptionalOnlyParameters() { $idToken = 'someRandomIdToken'; $postLogoutRedirectUri = 'https://overriden-post-logout-uri.net'; @@ -106,7 +109,7 @@ public function itShouldGenerateLogoutUrlWithOptionalOnlyParameters(): void /** * @test */ - public function itShouldGetBaseSessionLogoutUrl(): void + public function itShouldGetBaseSessionLogoutUrl() { $url = $this->provider->getBaseSessionLogoutUrl(); $uri = parse_url($url); @@ -122,7 +125,7 @@ public function itShouldGetBaseSessionLogoutUrl(): void /** * @test */ - public function itShouldGetLogoutUrl(): void + public function itShouldGetLogoutUrl() { $idToken = 'someRandomIdToken'; @@ -142,7 +145,7 @@ public function itShouldGetLogoutUrl(): void /** * @test */ - public function itShouldThrowExceptionWhenIdTokenIsMissing(): void + public function itShouldThrowExceptionWhenIdTokenIsMissing() { $this->expectException(\Exception::class); @@ -156,7 +159,7 @@ public function itShouldThrowExceptionWhenIdTokenIsMissing(): void /** * @test */ - public function itShouldThrowExceptionWhenPostLogoutCallbackUriIsMissing(): void + public function itShouldThrowExceptionWhenPostLogoutCallbackUriIsMissing() { $idToken = 'someRandomIdToken'; diff --git a/tests/src/Provider/PrestaShopTest.php b/tests/src/Provider/PrestaShopTest.php index 15a1444..7aa6d44 100644 --- a/tests/src/Provider/PrestaShopTest.php +++ b/tests/src/Provider/PrestaShopTest.php @@ -3,12 +3,13 @@ namespace PrestaShop\OAuth2\Client\Test\Provider; use GuzzleHttp\ClientInterface; +use GuzzleHttp\Psr7\Utils; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use League\OAuth2\Client\Token\AccessToken; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use PrestaShop\OAuth2\Client\Provider\PrestaShop; use PrestaShop\OAuth2\Client\Provider\PrestaShopUser; +use PrestaShop\OAuth2\Client\Provider\WellKnown; use Psr\Http\Message\ResponseInterface; class PrestaShopTest extends TestCase @@ -18,24 +19,39 @@ class PrestaShopTest extends TestCase */ private $provider; + /** + * @return void + */ protected function setUp(): void { - $this->provider = new PrestaShop([ - 'clientId' => 'test-client', - 'clientSecret' => 'secret', - 'redirectUri' => 'https://test-client-redirect.net', - 'uiLocales' => ['fr-CA', 'en'], - 'acrValues' => ['prompt:login'], - ]); + $this->provider = $this->getMockBuilder(PrestaShop::class) + ->setConstructorArgs([[ + 'clientId' => 'test-client', + 'clientSecret' => 'secret', + 'redirectUri' => 'https://test-client-redirect.net', + 'uiLocales' => ['fr-CA', 'en'], + 'acrValues' => ['prompt:login'], + ]]) + ->setMethods(['getWellKnown']) + ->getMock(); + + $oauthUrl = 'https://oauth.foo.bar'; + + $this->provider->method('getWellKnown') + ->willReturn(new WellKnown([ + 'authorization_endpoint' => $oauthUrl . '/oauth2/auth', + 'token_endpoint' => $oauthUrl . '/oauth2/token', + 'userinfo_endpoint' => $oauthUrl . '/userinfo', + ])); } /** - * @param string $responseBody - * @param int $statusCode + * @param $responseBody + * @param $statusCode * - * @return MockObject + * @return \PHPUnit_Framework_MockObject_MockObject|ResponseInterface|(ResponseInterface&\PHPUnit_Framework_MockObject_MockObject) */ - private function createMockResponse(string $responseBody, int $statusCode = 200): MockObject + private function createMockResponse($responseBody, $statusCode = 200) { $response = $this->createMock(ResponseInterface::class); @@ -43,11 +59,11 @@ private function createMockResponse(string $responseBody, int $statusCode = 200) ->willReturn($statusCode); $response->method('getBody') - ->willReturn($responseBody); + ->willReturn(Utils::streamFor($responseBody)); $response->method('getHeader') ->with('content-type') - ->willReturn('application/json'); + ->willReturn(['application/json']); return $response; } @@ -55,7 +71,7 @@ private function createMockResponse(string $responseBody, int $statusCode = 200) /** * @test */ - public function itShouldGenerateAuthorizationUrl(): void + public function itShouldGenerateAuthorizationUrl() { $url = $this->provider->getAuthorizationUrl(); $uri = parse_url($url); @@ -76,7 +92,7 @@ public function itShouldGenerateAuthorizationUrl(): void /** * @test */ - public function itShouldGetBaseAccessTokenUrl(): void + public function itShouldGetBaseAccessTokenUrl() { $params = []; @@ -94,7 +110,7 @@ public function itShouldGetBaseAccessTokenUrl(): void /** * @test */ - public function itShouldGetAuthorizationUrl(): void + public function itShouldGetAuthorizationUrl() { $url = $this->provider->getAuthorizationUrl(); $uri = parse_url($url); @@ -110,7 +126,7 @@ public function itShouldGetAuthorizationUrl(): void /** * @test */ - public function itShouldGetAccessTokenWithAuthorizationCode(): void + public function itShouldGetAccessTokenWithAuthorizationCode() { $response = $this->createMockResponse(<<createMockResponse(<<createMockResponse(<<createMockResponse(<<createMockResponse('{}', 403); diff --git a/tests/src/Provider/PrestaShopUserTest.php b/tests/src/Provider/PrestaShopUserTest.php index 8a287f8..7f65069 100644 --- a/tests/src/Provider/PrestaShopUserTest.php +++ b/tests/src/Provider/PrestaShopUserTest.php @@ -37,6 +37,9 @@ class PrestaShopUserTest extends TestCase ) */ + /** + * @return void + */ protected function setUp(): void { $this->user = new PrestaShopUser([ @@ -51,7 +54,7 @@ protected function setUp(): void /** * @test */ - public function itShouldGetId(): void + public function itShouldGetId() { $this->assertEquals('4rFN5bm2piPeHTYUFtUIwcyFKKKOp', $this->user->getId()); } @@ -59,7 +62,7 @@ public function itShouldGetId(): void /** * @test */ - public function itShouldGetEmail(): void + public function itShouldGetEmail() { $this->assertEquals('john.doe@prestashop.com', $this->user->getEmail()); } @@ -67,7 +70,7 @@ public function itShouldGetEmail(): void /** * @test */ - public function itShouldGetEmailVerified(): void + public function itShouldGetEmailVerified() { $this->assertEquals(1, $this->user->getEmailVerified()); } @@ -75,7 +78,7 @@ public function itShouldGetEmailVerified(): void /** * @test */ - public function itShouldGetName(): void + public function itShouldGetName() { $this->assertEquals('John Doe', $this->user->getName()); } @@ -83,7 +86,7 @@ public function itShouldGetName(): void /** * @test */ - public function itShouldGetPicture(): void + public function itShouldGetPicture() { $this->assertEquals('https://lh3.googleusercontent.com/a/AATXAJzK3D_K4_7YHFDQHFD3C_1ViDfRVDmQTukCyw=s96-c', $this->user->getPicture()); }