Merge branch 'V5-WIP' into move_identifier_generation

This commit is contained in:
Julián Gutiérrez 2016-02-18 18:14:59 +01:00
commit a644eacea7
15 changed files with 628 additions and 101 deletions

View File

@ -11,6 +11,7 @@ interface ClientEntityInterface
/**
* Set the client's identifier
*
* @param $identifier
*/
public function setIdentifier($identifier);
@ -23,7 +24,42 @@ interface ClientEntityInterface
/**
* Set the client's name
*
* @param string $name
*/
public function setName($name);
/**
* @param string $secret
*/
public function setSecret($secret);
/**
* Validate the secret provided by the client
*
* @param string $submittedSecret
*
* @return boolean
*/
public function validateSecret($submittedSecret);
/**
* Set the client's redirect uri
*
* @param string $redirectUri
*/
public function setRedirectUri($redirectUri);
/**
* Returns the registered redirect URI
*
* @return string
*/
public function getRedirectUri();
/**
* Returns true if the client is capable of keeping it's secrets secret
* @return boolean
*/
public function canKeepASecret();
}

View File

@ -9,8 +9,17 @@ trait ClientEntityTrait
protected $name;
/**
* Get the client's name
* @return string
* @var string
*/
protected $secret;
/**
* @var string
*/
protected $redirectUri;
/**
* @inheritdoc
*/
public function getName()
{
@ -18,11 +27,50 @@ trait ClientEntityTrait
}
/**
* Set the client's name
* @param string $name
* @inheritdoc
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @inheritdoc
*/
public function canKeepASecret()
{
return $this->secret !== null;
}
/**
* @inheritdoc
*/
public function setSecret($secret)
{
$this->secret = $secret;
}
/**
* @inheritdoc
*/
public function validateSecret($submittedSecret)
{
return strcmp((string) $submittedSecret, $this->secret) === 0;
}
/**
* @inheritdoc
*/
public function setRedirectUri($redirectUri)
{
$this->redirectUri = $redirectUri;
}
/**
* @inheritdoc
*/
public function getRedirectUri()
{
return $this->redirectUri;
}
}

View File

@ -21,8 +21,11 @@ use League\OAuth2\Server\Entities\RefreshTokenEntity;
use League\OAuth2\Server\Entities\ScopeEntity;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
use Psr\Http\Message\ServerRequestInterface;
/**
@ -54,6 +57,16 @@ abstract class AbstractGrant implements GrantTypeInterface
*/
protected $scopeRepository;
/**
* @var \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface
*/
private $authCodeRepository;
/**
* @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
*/
private $refreshTokenRepository;
/**
* @var string
*/
@ -93,6 +106,22 @@ abstract class AbstractGrant implements GrantTypeInterface
$this->scopeRepository = $scopeRepository;
}
/**
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
$this->refreshTokenRepository = $refreshTokenRepository;
}
/**
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
*/
public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
{
$this->authCodeRepository = $authCodeRepository;
}
/**
* @param string $pathToPrivateKey
*/
@ -125,21 +154,32 @@ abstract class AbstractGrant implements GrantTypeInterface
$this->refreshTokenTTL = $refreshTokenTTL;
}
/**
* @return AuthCodeRepositoryInterface
*/
protected function getAuthCodeRepository()
{
return $this->authCodeRepository;
}
/**
* @return RefreshTokenRepositoryInterface
*/
protected function getRefreshTokenRepository()
{
return $this->refreshTokenRepository;
}
/**
* Validate the client
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param bool $validateSecret
* @param bool $validateRedirectUri
*
* @return \League\OAuth2\Server\Entities\Interfaces\ClientEntityInterface
* @throws \League\OAuth2\Server\Exception\OAuthServerException
*/
protected function validateClient(
ServerRequestInterface $request,
$validateSecret = true,
$validateRedirectUri = false
) {
protected function validateClient(ServerRequestInterface $request)
{
$clientId = $this->getRequestParameter(
'client_id',
$request,
@ -149,30 +189,34 @@ abstract class AbstractGrant implements GrantTypeInterface
throw OAuthServerException::invalidRequest('client_id', null, '`%s` parameter is missing');
}
$client = $this->clientRepository->getClientEntity(
$clientId,
$this->getIdentifier()
);
if (!$client instanceof ClientEntityInterface) {
throw OAuthServerException::invalidClient();
}
// If the client is confidential require the client secret
$clientSecret = $this->getRequestParameter(
'client_secret',
$request,
$this->getServerParameter('PHP_AUTH_PW', $request)
);
if (is_null($clientSecret) && $validateSecret === true) {
if ($client->canKeepASecret() && is_null($clientSecret)) {
throw OAuthServerException::invalidRequest('client_secret', null, '`%s` parameter is missing');
}
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
if (is_null($redirectUri) && $validateRedirectUri === true) {
throw OAuthServerException::invalidRequest('redirect_uri', null, '`%s` parameter is missing');
if ($client->canKeepASecret() && $client->validateSecret($clientSecret) === false) {
$this->getEmitter()->emit(new Event('client.authentication.failed', $request));
throw OAuthServerException::invalidClient();
}
$client = $this->clientRepository->getClientEntity(
$clientId,
$clientSecret,
$redirectUri,
$this->getIdentifier()
);
if (!$client instanceof ClientEntityInterface) {
$this->getEmitter()->emit(new Event('client.authentication.failed', $request));
// If a redirect URI is provided ensure it matches what is pre-registered
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
if ($redirectUri !== null && (strcmp($client->getRedirectUri(), $redirectUri) !== 0)) {
throw OAuthServerException::invalidClient();
}
@ -303,6 +347,8 @@ abstract class AbstractGrant implements GrantTypeInterface
$accessToken->addScope($scope);
}
$this->accessTokenRepository->persistNewAccessToken($accessToken);
return $accessToken;
}
@ -336,6 +382,8 @@ abstract class AbstractGrant implements GrantTypeInterface
$authCode->addScope($scope);
}
$this->authCodeRepository->persistNewAuthCode($authCode);
return $authCode;
}
@ -351,6 +399,8 @@ abstract class AbstractGrant implements GrantTypeInterface
$refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
$refreshToken->setAccessToken($accessToken);
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
return $refreshToken;
}

View File

@ -23,10 +23,6 @@ class AuthCodeGrant extends AbstractGrant
* @var \DateInterval
*/
private $authCodeTTL;
/**
* @var \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface
*/
private $authCodeRepository;
/**
* @var \League\OAuth2\Server\Repositories\UserRepositoryInterface
@ -43,10 +39,6 @@ class AuthCodeGrant extends AbstractGrant
*/
private $pathToAuthorizeTemplate;
/**
* @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
*/
private $refreshTokenRepository;
/**
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
@ -64,8 +56,8 @@ class AuthCodeGrant extends AbstractGrant
$pathToLoginTemplate = null,
$pathToAuthorizeTemplate = null
) {
$this->authCodeRepository = $authCodeRepository;
$this->refreshTokenRepository = $refreshTokenRepository;
$this->setAuthCodeRepository($authCodeRepository);
$this->setRefreshTokenRepository($refreshTokenRepository);
$this->userRepository = $userRepository;
$this->authCodeTTL = $authCodeTTL;
$this->pathToLoginTemplate = ($pathToLoginTemplate === null)
@ -89,26 +81,7 @@ class AuthCodeGrant extends AbstractGrant
protected function respondToAuthorizationRequest(
ServerRequestInterface $request
) {
$clientId = $this->getQueryStringParameter(
'client_id',
$request,
$this->getServerParameter('PHP_AUTH_USER', $request)
);
if (is_null($clientId)) {
throw OAuthServerException::invalidRequest('client_id', null, '`%s` parameter is missing');
}
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request, null);
if (is_null($redirectUri)) {
throw OAuthServerException::invalidRequest('redirect_uri', null, '`%s` parameter is missing');
}
$client = $this->clientRepository->getClientEntity(
$clientId,
null,
$redirectUri,
$this->getIdentifier()
);
$client = $this->validateClient($request);
if ($client instanceof ClientEntityInterface === false) {
$this->emitter->emit(new Event('client.authentication.failed', $request));
@ -116,7 +89,7 @@ class AuthCodeGrant extends AbstractGrant
throw OAuthServerException::invalidClient();
}
$scopes = $this->validateScopes($request, $client, $redirectUri);
$scopes = $this->validateScopes($request, $client, $client->getRedirectUri());
$queryString = http_build_query($request->getQueryParams());
$postbackUri = new Uri(
sprintf(
@ -168,8 +141,9 @@ class AuthCodeGrant extends AbstractGrant
// The user hasn't logged in yet so show a login form
if ($userId === null) {
$engine = new Engine(dirname($this->pathToLoginTemplate));
$pathParts = explode(DIRECTORY_SEPARATOR, $this->pathToLoginTemplate);
$html = $engine->render(
'login_user',
end($pathParts),
[
'error' => $loginError,
'postback_uri' => (string) $postbackUri->withQuery($queryString),
@ -183,8 +157,9 @@ class AuthCodeGrant extends AbstractGrant
// The user hasn't approved the client yet so show an authorize form
if ($userId !== null && $userHasApprovedClient === null) {
$engine = new Engine(dirname($this->pathToAuthorizeTemplate));
$pathParts = explode(DIRECTORY_SEPARATOR, $this->pathToAuthorizeTemplate);
$html = $engine->render(
'authorize_client',
end($pathParts),
[
'client' => $client,
'scopes' => $scopes,
@ -212,7 +187,7 @@ class AuthCodeGrant extends AbstractGrant
$stateParameter = $this->getQueryStringParameter('state', $request);
$redirectUri = new Uri($redirectUri);
$redirectUri = new Uri($client->getRedirectUri());
parse_str($redirectUri->getQuery(), $redirectPayload);
if ($stateParameter !== null) {
$redirectPayload['state'] = $stateParameter;
@ -226,7 +201,6 @@ class AuthCodeGrant extends AbstractGrant
$redirectUri,
$scopes
);
$this->authCodeRepository->persistNewAuthCode($authCode);
$redirectPayload['code'] = KeyCrypt::encrypt(
json_encode(
@ -263,6 +237,12 @@ class AuthCodeGrant extends AbstractGrant
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
) {
// The redirect URI is required in this request
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request, null);
if (is_null($redirectUri)) {
throw OAuthServerException::invalidRequest('redirect_uri', null, '`%s` parameter is missing');
}
// Validate request
$client = $this->validateClient($request);
$encryptedAuthCode = $this->getRequestParameter('code', $request, null);
@ -278,7 +258,7 @@ class AuthCodeGrant extends AbstractGrant
throw OAuthServerException::invalidRequest('code', 'Authorization code has expired');
}
if ($this->authCodeRepository->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) {
if ($this->getAuthCodeRepository()->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) {
throw OAuthServerException::invalidRequest('code', 'Authorization code has been revoked');
}
@ -297,8 +277,6 @@ class AuthCodeGrant extends AbstractGrant
$authCodePayload->scopes
);
$refreshToken = $this->issueRefreshToken($accessToken);
$this->accessTokenRepository->persistNewAccessToken($accessToken);
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
// Inject tokens into response type
$responseType->setAccessToken($accessToken);

View File

@ -33,7 +33,6 @@ class ClientCredentialsGrant extends AbstractGrant
// Issue and persist access token
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $client->getIdentifier(), $scopes);
$this->accessTokenRepository->persistNewAccessToken($accessToken);
// Inject access token into response type
$responseType->setAccessToken($accessToken);

View File

@ -29,11 +29,6 @@ class PasswordGrant extends AbstractGrant
*/
private $userRepository;
/**
* @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
*/
private $refreshTokenRepository;
/**
* @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
@ -43,7 +38,7 @@ class PasswordGrant extends AbstractGrant
RefreshTokenRepositoryInterface $refreshTokenRepository
) {
$this->userRepository = $userRepository;
$this->refreshTokenRepository = $refreshTokenRepository;
$this->setRefreshTokenRepository($refreshTokenRepository);
$this->refreshTokenTTL = new \DateInterval('P1M');
}
@ -64,8 +59,6 @@ class PasswordGrant extends AbstractGrant
// Issue and persist new tokens
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes);
$refreshToken = $this->issueRefreshToken($accessToken);
$this->accessTokenRepository->persistNewAccessToken($accessToken);
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
// Inject tokens into response
$responseType->setAccessToken($accessToken);

View File

@ -12,6 +12,7 @@
namespace League\OAuth2\Server\Grant;
use League\Event\Event;
use League\OAuth2\Server\Entities\ScopeEntity;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
@ -23,17 +24,12 @@ use Psr\Http\Message\ServerRequestInterface;
*/
class RefreshTokenGrant extends AbstractGrant
{
/**
* @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
*/
private $refreshTokenRepository;
/**
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
$this->refreshTokenRepository = $refreshTokenRepository;
$this->setRefreshTokenRepository($refreshTokenRepository);
$this->refreshTokenTTL = new \DateInterval('P1M');
}
@ -47,13 +43,17 @@ class RefreshTokenGrant extends AbstractGrant
\DateInterval $accessTokenTTL
) {
// Validate request
$client = $this->validateClient($request);
$client = $this->validateClient($request);
$oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier());
$scopes = $this->validateScopes($request, $client);
$scopes = $this->validateScopes($request, $client);
// If no new scopes are requested then give the access token the original session scopes
if (count($scopes) === 0) {
$scopes = $oldRefreshToken['scopes'];
$scopes = array_map(function ($scopeId) {
$scope = new ScopeEntity();
$scope->setIdentifier($scopeId);
return $scope;
}, $oldRefreshToken['scopes']);
} else {
// The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure
// the request doesn't include any new scopes
@ -68,13 +68,13 @@ class RefreshTokenGrant extends AbstractGrant
// Expire old tokens
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
$this->getRefreshTokenRepository()->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
// Issue and persist new tokens
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
$refreshToken = $this->issueRefreshToken($accessToken);
$this->accessTokenRepository->persistNewAccessToken($accessToken);
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
$this->getRefreshTokenRepository()->persistNewRefreshToken($refreshToken);
// Inject tokens into response
$responseType->setAccessToken($accessToken);
@ -120,7 +120,7 @@ class RefreshTokenGrant extends AbstractGrant
throw OAuthServerException::invalidRefreshToken('Token has expired');
}
if ($this->refreshTokenRepository->isRefreshTokenRevoked($refreshTokenData['refresh_token_id']) === true) {
if ($this->getRefreshTokenRepository()->isRefreshTokenRevoked($refreshTokenData['refresh_token_id']) === true) {
throw OAuthServerException::invalidRefreshToken('Token has been revoked');
}

View File

@ -19,12 +19,10 @@ interface ClientRepositoryInterface extends RepositoryInterface
/**
* Get a client
*
* @param string $grantType The grant type used
* @param string $clientIdentifier The client's identifier
* @param string|null $clientSecret The client's secret
* @param string|null $redirectUri The client's redirect URI
* @param string $grantType The grant type used
*
* @return \League\OAuth2\Server\Entities\Interfaces\ClientEntityInterface
*/
public function getClientEntity($grantType, $clientIdentifier, $clientSecret = null, $redirectUri = null);
public function getClientEntity($clientIdentifier, $grantType);
}

View File

@ -10,33 +10,56 @@ use League\OAuth2\Server\Entities\Interfaces\AuthCodeEntityInterface;
use League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntity;
use League\OAuth2\Server\Grant\AbstractGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
use Zend\Diactoros\ServerRequest;
class AbstractGrantTest extends \PHPUnit_Framework_TestCase
{
public function testGetSet()
{
$clientRepositoryMock = $this->getMock(ClientRepositoryInterface::class);
$accessTokenRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class);
$scopeRepositoryMock = $this->getMock(ScopeRepositoryInterface::class);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setPathToPrivateKey('./private.key');
$grantMock->setPathToPublicKey('./public.key');
$grantMock->setEmitter(new Emitter());
}
public function testValidateClientPublic()
{
$client = new ClientEntity();
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
$grantMock->setAccessTokenRepository($accessTokenRepositoryMock);
$grantMock->setScopeRepository($scopeRepositoryMock);
$grantMock->setPathToPrivateKey('./private.key');
$grantMock->setPathToPublicKey('./public.key');
$grantMock->setEmitter(new Emitter());
$grantMock->setRefreshTokenTTL(new \DateInterval('PT1H'));
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody(
[
'client_id' => 'foo',
]
);
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
$result = $validateClientMethod->invoke($grantMock, $serverRequest, true, true);
$this->assertEquals($client, $result);
}
public function testValidateClient()
public function testValidateClientConfidential()
{
$client = new ClientEntity();
$client->setSecret('bar');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
@ -89,6 +112,7 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
public function testValidateClientMissingClientSecret()
{
$client = new ClientEntity();
$client->setSecret('bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
@ -112,9 +136,10 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
*/
public function testValidateClientMissingRedirectUri()
public function testValidateClientInvalidClientSecret()
{
$client = new ClientEntity();
$client->setSecret('bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
@ -124,6 +149,60 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody([
'client_id' => 'foo',
'client_secret' => 'foo',
]);
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
*/
public function testValidateClientInvalidRedirectUri()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody([
'client_id' => 'foo',
'redirect_uri' => 'http://bar/foo'
]);
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
*/
public function testValidateClientBadClient()
{
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn(null);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setClientRepository($clientRepositoryMock);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody([
'client_id' => 'foo',
@ -133,7 +212,7 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
$validateClientMethod->setAccessible(true);
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
$validateClientMethod->invoke($grantMock, $serverRequest, true);
}
public function testCanRespondToRequest()
@ -151,9 +230,12 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
public function testIssueRefreshToken()
{
$refreshTokenRepoMock = $this->getMock(RefreshTokenRepositoryInterface::class);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setRefreshTokenTTL(new \DateInterval('PT1M'));
$grantMock->setRefreshTokenRepository($refreshTokenRepoMock);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken');
@ -169,8 +251,11 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
public function testIssueAccessToken()
{
$accessTokenRepoMock = $this->getMock(AccessTokenRepositoryInterface::class);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setAccessTokenRepository($accessTokenRepoMock);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$issueAccessTokenMethod = $abstractGrantReflection->getMethod('issueAccessToken');
@ -190,8 +275,11 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
public function testIssueAuthCode()
{
$authCodeRepoMock = $this->getMock(AuthCodeRepositoryInterface::class);
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$grantMock->setAuthCodeRepository($authCodeRepoMock);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$issueAuthCodeMethod = $abstractGrantReflection->getMethod('issueAuthCode');
@ -286,4 +374,15 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
$grantMock->validateScopes($serverRequest, new ClientEntity());
}
public function testGenerateUniqueIdentifier()
{
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$method = $abstractGrantReflection->getMethod('generateUniqueIdentifier');
$method->setAccessible(true);
$this->assertTrue(is_string($method->invoke($grantMock)));
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace LeagueTests\Grant;
use League\OAuth2\Server\Entities\ClientEntity;
use League\OAuth2\Server\Entities\Interfaces\AccessTokenEntityInterface;
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use LeagueTests\Stubs\StubResponseType;
use Zend\Diactoros\ServerRequest;
class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase
{
public function testGetIdentifier()
{
$grant = new ClientCredentialsGrant();
$this->assertEquals('client_credentials', $grant->getIdentifier());
}
public function testRespondToRequest()
{
$client = new ClientEntity();
$client->setSecret('bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$grant = new ClientCredentialsGrant();
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody(
[
'client_id' => 'foo',
'client_secret' => 'bar',
]
);
$responseType = new StubResponseType();
$grant->respondToRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
$this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface);
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace LeagueTests\Grant;
use League\OAuth2\Server\Entities\ClientEntity;
use League\OAuth2\Server\Entities\Interfaces\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface;
use League\OAuth2\Server\Grant\PasswordGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use LeagueTests\Stubs\StubResponseType;
use LeagueTests\Stubs\UserEntity;
use Zend\Diactoros\ServerRequest;
class PasswordGrantTest extends \PHPUnit_Framework_TestCase
{
public function testGetIdentifier()
{
$userRepositoryMock = $this->getMock(UserRepositoryInterface::class);
$refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class);
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
$this->assertEquals('password', $grant->getIdentifier());
}
public function testRespondToRequest()
{
$client = new ClientEntity();
$client->setSecret('bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
$userEntity = new UserEntity();
$userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity);
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody(
[
'client_id' => 'foo',
'client_secret' => 'bar',
'username' => 'foo',
'password' => 'bar',
]
);
$responseType = new StubResponseType();
$grant->respondToRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
$this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface);
$this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace LeagueTests\Grant;
use League\OAuth2\Server\Entities\ClientEntity;
use League\OAuth2\Server\Entities\Interfaces\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface;
use League\OAuth2\Server\Grant\PasswordGrant;
use League\OAuth2\Server\Grant\RefreshTokenGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use League\OAuth2\Server\Utils\KeyCrypt;
use LeagueTests\Stubs\StubResponseType;
use LeagueTests\Stubs\UserEntity;
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
use Zend\Diactoros\ServerRequest;
class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase
{
public function testGetIdentifier()
{
$refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class);
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
$this->assertEquals('refresh_token', $grant->getIdentifier());
}
public function testRespondToRequest()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setSecret('bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
$userEntity = new UserEntity();
$userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity);
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setPathToPublicKey('file://'.__DIR__.'/../Utils/public.key');
$grant->setPathToPrivateKey('file://'.__DIR__.'/../Utils/private.key');
$oldRefreshToken = KeyCrypt::encrypt(
json_encode(
[
'client_id' => 'foo',
'refresh_token_id' => 'zyxwvu',
'access_token_id' => 'abcdef',
'scopes' => ['foo'],
'user_id' => 123,
'expire_time' => time() + 3600,
]
),
'file://'.__DIR__.'/../Utils/private.key'
);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withParsedBody(
[
'client_id' => 'foo',
'client_secret' => 'bar',
'refresh_token' => $oldRefreshToken,
]
);
$responseType = new StubResponseType();
$grant->respondToRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
$this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface);
$this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
}

56
tests/ServerTest.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace LeagueTests;
use League\OAuth2\Server\Entities\ClientEntity;
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
use League\OAuth2\Server\Server;
use LeagueTests\Stubs\StubResponseType;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use Psr\Http\Message\ResponseInterface;
class ServerTest extends \PHPUnit_Framework_TestCase
{
public function testRespondToRequestInvalidGrantType()
{
$server = new Server(
$this->getMock(ClientRepositoryInterface::class),
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
'',
'',
new StubResponseType()
);
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
$response = $server->respondToRequest();
$this->assertTrue($response instanceof ResponseInterface);
$this->assertEquals(400, $response->getStatusCode());
}
public function testRespondToRequest()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
$server = new Server(
$clientRepository,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
'',
'',
new StubResponseType()
);
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
$_POST['grant_type'] = 'client_credentials';
$_POST['client_id'] = 'foo';
$_POST['client_secret'] = 'bar';
$response = $server->respondToRequest();
$this->assertEquals(200, $response->getStatusCode());
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace LeagueTests\Stubs;
use League\OAuth2\Server\Entities\Interfaces\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface;
use League\OAuth2\Server\ResponseTypes\AbstractResponseType;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response;
class StubResponseType extends AbstractResponseType
{
public function __construct() {}
public function getAccessToken()
{
return $this->accessToken;
}
public function getRefreshToken()
{
return $this->refreshToken;
}
/**
* @param \League\OAuth2\Server\Entities\Interfaces\AccessTokenEntityInterface $accessToken
*/
public function setAccessToken(AccessTokenEntityInterface $accessToken)
{
$this->accessToken = $accessToken;
}
/**
* @param \League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface $refreshToken
*/
public function setRefreshToken(RefreshTokenEntityInterface $refreshToken)
{
$this->refreshToken = $refreshToken;
}
/**
* @param ServerRequestInterface $request
*
* @return ServerRequestInterface
*/
public function determineAccessTokenInHeader(ServerRequestInterface $request)
{
// TODO: Implement determineAccessTokenInHeader() method.
}
/**
* @param ResponseInterface $response
*
* @return ResponseInterface
*/
public function generateHttpResponse(ResponseInterface $response)
{
return new Response();
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace LeagueTests\Stubs;
use League\OAuth2\Server\Entities\Interfaces\UserEntityInterface;
class UserEntity implements UserEntityInterface
{
public function getIdentifier()
{
return 123;
}
}