Make AuthorizationServer stateless

This commit is contained in:
Marc Bennewitz 2018-11-02 15:38:07 +01:00
parent a34f5dd7db
commit d288a2ad8a
2 changed files with 78 additions and 20 deletions

View File

@ -49,9 +49,9 @@ class AuthorizationServer implements EmitterAwareInterface
protected $publicKey; protected $publicKey;
/** /**
* @var null|ResponseTypeInterface * @var ResponseTypeInterface
*/ */
protected $responseType; protected $responseTypePrototype;
/** /**
* @var ClientRepositoryInterface * @var ClientRepositoryInterface
@ -86,7 +86,7 @@ class AuthorizationServer implements EmitterAwareInterface
* @param ScopeRepositoryInterface $scopeRepository * @param ScopeRepositoryInterface $scopeRepository
* @param CryptKey|string $privateKey * @param CryptKey|string $privateKey
* @param string|Key $encryptionKey * @param string|Key $encryptionKey
* @param null|ResponseTypeInterface $responseType * @param null|ResponseTypeInterface $responseTypePrototype
*/ */
public function __construct( public function __construct(
ClientRepositoryInterface $clientRepository, ClientRepositoryInterface $clientRepository,
@ -94,7 +94,7 @@ class AuthorizationServer implements EmitterAwareInterface
ScopeRepositoryInterface $scopeRepository, ScopeRepositoryInterface $scopeRepository,
$privateKey, $privateKey,
$encryptionKey, $encryptionKey,
ResponseTypeInterface $responseType = null ResponseTypeInterface $responseTypePrototype = null
) { ) {
$this->clientRepository = $clientRepository; $this->clientRepository = $clientRepository;
$this->accessTokenRepository = $accessTokenRepository; $this->accessTokenRepository = $accessTokenRepository;
@ -105,7 +105,17 @@ class AuthorizationServer implements EmitterAwareInterface
} }
$this->privateKey = $privateKey; $this->privateKey = $privateKey;
$this->encryptionKey = $encryptionKey; $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( $tokenResponse = $grantType->respondToAccessTokenRequest(
$request, $request,
$this->getResponseType(), $this->newResponseType(),
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
); );
@ -202,18 +212,9 @@ class AuthorizationServer implements EmitterAwareInterface
* *
* @return ResponseTypeInterface * @return ResponseTypeInterface
*/ */
protected function getResponseType() protected function newResponseType()
{ {
if ($this->responseType instanceof ResponseTypeInterface === false) { return clone $this->responseTypePrototype;
$this->responseType = new BearerTokenResponse();
}
if ($this->responseType instanceof AbstractResponseType === true) {
$this->responseType->setPrivateKey($this->privateKey);
}
$this->responseType->setEncryptionKey($this->encryptionKey);
return $this->responseType;
} }
/** /**

View File

@ -3,6 +3,7 @@
namespace LeagueTests; namespace LeagueTests;
use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\Grant\ClientCredentialsGrant; use League\OAuth2\Server\Grant\ClientCredentialsGrant;
@ -90,7 +91,7 @@ class AuthorizationServerTest extends TestCase
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
} }
public function testGetResponseType() public function testNewDefaultResponseType()
{ {
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); $clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
@ -103,10 +104,66 @@ class AuthorizationServerTest extends TestCase
); );
$abstractGrantReflection = new \ReflectionClass($server); $abstractGrantReflection = new \ReflectionClass($server);
$method = $abstractGrantReflection->getMethod('getResponseType'); $method = $abstractGrantReflection->getMethod('newResponseType');
$method->setAccessible(true); $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() public function testCompleteAuthorizationRequest()