From cc3a24497be85368c24b321020f14b9544e85e7a Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 4 Dec 2024 21:53:52 +0330 Subject: [PATCH] bypass bc breaking change and add tests --- src/Entities/ClientEntityInterface.php | 6 ++- src/Entities/Traits/ClientTrait.php | 4 +- src/Grant/AbstractGrant.php | 13 ++++++- tests/Grant/AbstractGrantTest.php | 53 ++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/Entities/ClientEntityInterface.php b/src/Entities/ClientEntityInterface.php index 138e831f7..beba39d6b 100644 --- a/src/Entities/ClientEntityInterface.php +++ b/src/Entities/ClientEntityInterface.php @@ -40,7 +40,9 @@ public function getRedirectUri(): string|array; public function isConfidential(): bool; /** - * Returns true if the client handles the given grant type. + * Returns true if the client supports the given grant type. + * + * To be added in a future major release. */ - public function hasGrantType(string $grantType): bool; + // public function supportsGrantType(string $grantType): bool; } diff --git a/src/Entities/Traits/ClientTrait.php b/src/Entities/Traits/ClientTrait.php index 0b4327c35..ada53fa5a 100644 --- a/src/Entities/Traits/ClientTrait.php +++ b/src/Entities/Traits/ClientTrait.php @@ -54,9 +54,9 @@ public function isConfidential(): bool } /** - * Returns true if the client handles the given grant type. + * Returns true if the client supports the given grant type. */ - public function hasGrantType(string $grantType): bool + public function supportsGrantType(string $grantType): bool { return true; } diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index c2e67cb28..7c27e95c5 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -189,13 +189,22 @@ protected function getClientEntityOrFail(string $clientId, ServerRequestInterfac throw OAuthServerException::invalidClient($request); } - if (!$client->hasGrantType($this->getIdentifier())) { + if ($this->supportsGrantType($client, $this->getIdentifier()) === false) { throw OAuthServerException::unauthorizedClient(); } return $client; } + /** + * Returns true if the given client is authorized to use the given grant type. + */ + protected function supportsGrantType(ClientEntityInterface $client, string $grantType): bool + { + return method_exists($client, 'supportsGrantType') === false + || $client->supportsGrantType($grantType) === true; + } + /** * Gets the client credentials from the request from the request body or * the Http Basic Authorization header @@ -488,7 +497,7 @@ protected function issueAuthCode( */ protected function issueRefreshToken(AccessTokenEntityInterface $accessToken): ?RefreshTokenEntityInterface { - if (!$accessToken->getClient()->hasGrantType('refresh_token')) { + if ($this->supportsGrantType($accessToken->getClient(), 'refresh_token') === false) { return null; } diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index e46cd0419..c13c2d2e5 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -289,6 +289,32 @@ public function testValidateClientBadClient(): void $validateClientMethod->invoke($grantMock, $serverRequest, true); } + public function testUnauthorizedClient(): void + { + $client = $this->getMockBuilder(ClientEntity::class)->getMock(); + $client->method('supportsGrantType')->willReturn(false); + + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock + ->expects(self::once()) + ->method('getClientEntity') + ->with('foo') + ->willReturn($client); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setClientRepository($clientRepositoryMock); + + $abstractGrantReflection = new ReflectionClass($grantMock); + + $getClientEntityOrFailMethod = $abstractGrantReflection->getMethod('getClientEntityOrFail'); + $getClientEntityOrFailMethod->setAccessible(true); + + $this->expectException(OAuthServerException::class); + + $getClientEntityOrFailMethod->invoke($grantMock, 'foo', new ServerRequest()); + } + public function testCanRespondToRequest(): void { $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); @@ -350,6 +376,33 @@ public function testIssueNullRefreshToken(): void self::assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken)); } + public function testIssueNullRefreshTokenUnauthorizedClient(): void + { + $client = $this->getMockBuilder(ClientEntity::class)->getMock(); + $client + ->expects(self::once()) + ->method('supportsGrantType') + ->with('refresh_token') + ->willReturn(false); + + $refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); + $refreshTokenRepoMock->expects(self::never())->method('getNewRefreshToken'); + + /** @var AbstractGrant $grantMock */ + $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setRefreshTokenTTL(new DateInterval('PT1M')); + $grantMock->setRefreshTokenRepository($refreshTokenRepoMock); + + $abstractGrantReflection = new ReflectionClass($grantMock); + $issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken'); + $issueRefreshTokenMethod->setAccessible(true); + + $accessToken = new AccessTokenEntity(); + $accessToken->setClient($client); + + self::assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken)); + } + public function testIssueAccessToken(): void { $accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();