From d288a2ad8a574d9a1b641d529a79cbc617e03305 Mon Sep 17 00:00:00 2001 From: Marc Bennewitz Date: Fri, 2 Nov 2018 15:38:07 +0100 Subject: [PATCH] Make AuthorizationServer stateless --- src/AuthorizationServer.php | 35 ++++++++--------- tests/AuthorizationServerTest.php | 63 +++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index f1e96146..a5ee1b02 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -49,9 +49,9 @@ class AuthorizationServer implements EmitterAwareInterface protected $publicKey; /** - * @var null|ResponseTypeInterface + * @var ResponseTypeInterface */ - protected $responseType; + protected $responseTypePrototype; /** * @var ClientRepositoryInterface @@ -86,7 +86,7 @@ class AuthorizationServer implements EmitterAwareInterface * @param ScopeRepositoryInterface $scopeRepository * @param CryptKey|string $privateKey * @param string|Key $encryptionKey - * @param null|ResponseTypeInterface $responseType + * @param null|ResponseTypeInterface $responseTypePrototype */ public function __construct( ClientRepositoryInterface $clientRepository, @@ -94,7 +94,7 @@ class AuthorizationServer implements EmitterAwareInterface ScopeRepositoryInterface $scopeRepository, $privateKey, $encryptionKey, - ResponseTypeInterface $responseType = null + ResponseTypeInterface $responseTypePrototype = null ) { $this->clientRepository = $clientRepository; $this->accessTokenRepository = $accessTokenRepository; @@ -105,7 +105,17 @@ class AuthorizationServer implements EmitterAwareInterface } $this->privateKey = $privateKey; $this->encryptionKey = $encryptionKey; - $this->responseType = $responseType; + + if ($responseTypePrototype === null) { + $responseTypePrototype = new BearerTokenResponse(); + } else { + $responseTypePrototype = clone $responseTypePrototype; + } + if ($responseTypePrototype instanceof AbstractResponseType) { + $responseTypePrototype->setPrivateKey($this->privateKey); + } + $responseTypePrototype->setEncryptionKey($this->encryptionKey); + $this->responseTypePrototype = $responseTypePrototype; } /** @@ -185,7 +195,7 @@ class AuthorizationServer implements EmitterAwareInterface } $tokenResponse = $grantType->respondToAccessTokenRequest( $request, - $this->getResponseType(), + $this->newResponseType(), $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] ); @@ -202,18 +212,9 @@ class AuthorizationServer implements EmitterAwareInterface * * @return ResponseTypeInterface */ - protected function getResponseType() + protected function newResponseType() { - if ($this->responseType instanceof ResponseTypeInterface === false) { - $this->responseType = new BearerTokenResponse(); - } - - if ($this->responseType instanceof AbstractResponseType === true) { - $this->responseType->setPrivateKey($this->privateKey); - } - $this->responseType->setEncryptionKey($this->encryptionKey); - - return $this->responseType; + return clone $this->responseTypePrototype; } /** diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index b003c23f..9e485501 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -3,6 +3,7 @@ namespace LeagueTests; use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Grant\ClientCredentialsGrant; @@ -90,7 +91,7 @@ class AuthorizationServerTest extends TestCase $this->assertEquals(200, $response->getStatusCode()); } - public function testGetResponseType() + public function testNewDefaultResponseType() { $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); @@ -103,10 +104,66 @@ class AuthorizationServerTest extends TestCase ); $abstractGrantReflection = new \ReflectionClass($server); - $method = $abstractGrantReflection->getMethod('getResponseType'); + $method = $abstractGrantReflection->getMethod('newResponseType'); $method->setAccessible(true); - $this->assertInstanceOf(BearerTokenResponse::class, $method->invoke($server)); + $responseTypeA = $method->invoke($server); + $responseTypeB = $method->invoke($server); + $this->assertInstanceOf(BearerTokenResponse::class, $responseTypeA); + $this->assertInstanceOf(BearerTokenResponse::class, $responseTypeB); + $this->assertNotSame($responseTypeA, $responseTypeB); + } + + public function testNewResponseTypeFromPrototype() + { + $privateKey = 'file://' . __DIR__ . '/Stubs/private.key'; + $encryptionKey = 'file://' . __DIR__ . '/Stubs/public.key'; + + $responseTypePrototype = new class extends BearerTokenResponse { + /* @return null|CryptKey */ + public function getPrivateKey() + { + return $this->privateKey; + } + + public function getEncryptionKey() + { + return $this->encryptionKey; + } + }; + + $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + + $server = new AuthorizationServer( + $clientRepository, + $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), + $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), + $privateKey, + $encryptionKey, + $responseTypePrototype + ); + + $abstractGrantReflection = new \ReflectionClass($server); + $method = $abstractGrantReflection->getMethod('newResponseType'); + $method->setAccessible(true); + + $responseTypeA = $method->invoke($server); + $responseTypeB = $method->invoke($server); + + // prototype should not get changed + $this->assertNull($responseTypePrototype->getPrivateKey()); + $this->assertNull($responseTypePrototype->getEncryptionKey()); + + // generated instances should have keys setup + $this->assertSame($privateKey, $responseTypeA->getPrivateKey()->getKeyPath()); + $this->assertSame($encryptionKey, $responseTypeA->getEncryptionKey()); + + // all instances should be different but based on the same prototype + $this->assertSame(get_class($responseTypePrototype), get_class($responseTypeA)); + $this->assertSame(get_class($responseTypePrototype), get_class($responseTypeB)); + $this->assertNotSame($responseTypePrototype, $responseTypeA); + $this->assertNotSame($responseTypePrototype, $responseTypeB); + $this->assertNotSame($responseTypeA, $responseTypeB); } public function testCompleteAuthorizationRequest()