diff --git a/Makefile b/Makefile index 180f0496c..797e7fc48 100644 --- a/Makefile +++ b/Makefile @@ -282,8 +282,16 @@ php-scoper-dump-autoload: php-scoper-fix-autoload: php fix-autoload.php +# segmentio/analytics-php is a non-scoped (psr0) lib shipped as-is: rewrite the +# deprecated "${var}" string interpolation (deprecated PHP 8.2, fatal PHP 9.0) +# into "{$var}". Runs on the freshly installed vendor, before bundling. +vendor-patch-segmentio: + @echo "patching segmentio interpolation deprecations (PHP 8.2+/9.0)..." + find ./vendor/segmentio/analytics-php/lib -type f -name '*.php' -exec \ + sed -i -E 's/\$$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/{$$\1}/g' {} \; + #php-scoper: php-scoper-add-prefix php-scoper-update-prefix php-scoper-dump-autoload php-scoper-fix-autoload -php-scoper: php-scoper-add-prefix php-scoper-dump-autoload php-scoper-fix-autoload +php-scoper: php-scoper-add-prefix vendor-patch-segmentio php-scoper-dump-autoload php-scoper-fix-autoload ########## # BUNDLING diff --git a/src/Account/Session/ShopSession.php b/src/Account/Session/ShopSession.php index cd13c635c..1c99e2bd7 100644 --- a/src/Account/Session/ShopSession.php +++ b/src/Account/Session/ShopSession.php @@ -76,22 +76,22 @@ public function __construct( /** * @param bool $forceRefresh * @param bool $throw - * @param array|null $scope - * @param array|null $audience + * @param array $scope + * @param array $audience * * @return Token * * @throws RefreshTokenException */ - public function getValidToken($forceRefresh = false, $throw = true, array $scope = null, array $audience = null) + public function getValidToken($forceRefresh = false, $throw = true, array $scope = [], array $audience = []) { - if ($scope === null) { + if (empty($scope)) { $scope = ($this->getStatusManager()->identityVerified() ? [ 'shop.verified', ] : []); } - if ($audience === null) { + if (empty($audience)) { $audience = [ 'store/' . $this->getStatusManager()->getCloudShopId(), $this->tokenAudience, diff --git a/src/Account/StatusManager.php b/src/Account/StatusManager.php index 436fbde73..5cc0923b8 100644 --- a/src/Account/StatusManager.php +++ b/src/Account/StatusManager.php @@ -188,7 +188,7 @@ public function invalidateCache() * * @return bool */ - public function cacheInvalidated(CachedShopStatus $cachedStatus = null) + public function cacheInvalidated($cachedStatus = null) { try { $cachedStatus = $cachedStatus ?: $this->getCachedStatus(); @@ -206,7 +206,7 @@ public function cacheInvalidated(CachedShopStatus $cachedStatus = null) * * @return bool */ - public function cacheExpired(CachedShopStatus $cachedStatus = null, $cacheTtl = self::CACHE_TTL) + public function cacheExpired($cachedStatus = null, $cacheTtl = self::CACHE_TTL) { try { //$dateUpd = $this->getCacheDateUpd(); diff --git a/src/Adapter/Link.php b/src/Adapter/Link.php index e88625e16..23a3fa021 100644 --- a/src/Adapter/Link.php +++ b/src/Adapter/Link.php @@ -45,7 +45,7 @@ class Link */ public function __construct( ShopContext $shopContext, - \Link $link = null + $link = null ) { if (null === $link) { $link = new \Link(); diff --git a/src/Repository/ConfigurationRepository.php b/src/Repository/ConfigurationRepository.php index a2c45824a..0de71e5af 100644 --- a/src/Repository/ConfigurationRepository.php +++ b/src/Repository/ConfigurationRepository.php @@ -37,7 +37,7 @@ class ConfigurationRepository * * @throws \Exception */ - public function __construct(Configuration $configuration = null) + public function __construct($configuration = null) { $this->configuration = $configuration; } diff --git a/tests/src/Unit/Account/Session/ShopSession/GetValidTokenTest.php b/tests/src/Unit/Account/Session/ShopSession/GetValidTokenTest.php index 59cf994f1..ca2e73f77 100644 --- a/tests/src/Unit/Account/Session/ShopSession/GetValidTokenTest.php +++ b/tests/src/Unit/Account/Session/ShopSession/GetValidTokenTest.php @@ -222,6 +222,101 @@ public function itShouldRefreshInvalidVerifiedShopToken(Token $invalidAccessToke $this->assertEquals((string) $this->validAccessToken, (string) $this->shopSession->getValidToken()); } + /** + * @test + */ + public function itShouldApplyDefaultScopeAndAudienceWhenNoneProvided() + { + $this->configurationRepository->updateCachedShopStatus(json_encode((new CachedShopStatus([ + 'isValid' => true, + 'updatedAt' => (new \DateTime())->format(\DateTime::ATOM), + 'shopStatus' => new ShopStatus([ + 'cloudShopId' => $this->cloudShopId, + 'isVerified' => true, + ]) + ]))->toArray())); + + list($shopSession, $tokenAudience, $capture) = $this->makeCapturingShopSession(); + + // no scope/audience provided + forced refresh => defaults must be resolved + $shopSession->getValidToken(true); + + $this->assertEquals(['shop.verified'], $capture->scope); + $this->assertEquals([ + 'store/' . $this->cloudShopId, + $tokenAudience, + ], $capture->audience); + + $shopSession->cleanup(); + } + + /** + * @test + */ + public function itShouldForwardExplicitScopeAndAudienceUnchanged() + { + $this->configurationRepository->updateCachedShopStatus(json_encode((new CachedShopStatus([ + 'isValid' => true, + 'updatedAt' => (new \DateTime())->format(\DateTime::ATOM), + 'shopStatus' => new ShopStatus([ + 'cloudShopId' => $this->cloudShopId, + 'isVerified' => true, + ]) + ]))->toArray())); + + list($shopSession, $tokenAudience, $capture) = $this->makeCapturingShopSession(); + + $scope = ['custom.scope']; + $audience = ['store/custom-audience']; + + // explicit non-empty scope/audience must be forwarded as-is (no default override) + $shopSession->getValidToken(true, true, $scope, $audience); + + $this->assertEquals($scope, $capture->scope); + $this->assertEquals($audience, $capture->audience); + + $shopSession->cleanup(); + } + + /** + * Builds a ShopSession whose OAuth2Service captures the scope/audience + * actually forwarded to getAccessTokenByClientCredentials(). + * + * @return array [ShopSession, string $tokenAudience, object $capture] + */ + private function makeCapturingShopSession() + { + $capture = new \stdClass(); + $capture->scope = null; + $capture->audience = null; + + $accessToken = new AccessToken([ + 'access_token' => (string) $this->validAccessToken, + ]); + + $oAuth2Service = $this->createMock(OAuth2Service::class); + $oAuth2Service->method('getOAuth2Client') + ->willReturn($this->oauth2Client); + $oAuth2Service->method('getAccessTokenByClientCredentials') + ->willReturnCallback(function ($scope, $audience) use ($capture, $accessToken) { + $capture->scope = $scope; + $capture->audience = $audience; + + return $accessToken; + }); + + $tokenAudience = $this->module->getParameter('ps_accounts.accounts_api_url'); + + $shopSession = new ShopSession( + $this->configurationRepository, + $oAuth2Service, + $tokenAudience + ); + $shopSession->cleanup(); + + return [$shopSession, $tokenAudience, $capture]; + } + /** * @test */