Skip to content

Commit

Permalink
Merge branch 'hotfix/expired-token-auth'
Browse files Browse the repository at this point in the history
Prevents invalid tokens from being used for authentication.
  • Loading branch information
Ocramius committed Apr 26, 2014
2 parents 51a7e2b + 03bd5f1 commit 2ca5bb1
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 60 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
"zfr/zfr-oauth2-server": "0.1.*"
},
"require-dev": {
"phpunit/phpunit": "~3.7",
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "1.4.*",
"zendframework/zend-view": "~2.2",
"satooshi/php-coveralls": "~0.6"
},
"autoload": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
namespace ZfrOAuth2Module\Server\Authentication\Storage;

use Zend\Authentication\Storage\NonPersistent;
use Zend\Http\Request as HttpRequest;
use Zend\Http\Request;
use Zend\Mvc\Application;
use ZfrOAuth2\Server\ResourceServer;

/**
Expand All @@ -34,42 +35,57 @@ class AccessTokenStorage extends NonPersistent
protected $resourceServer;

/**
* @var HttpRequest
* @var Application
*/
protected $request;
private $application;

/**
* @param ResourceServer $resourceServer
* @param Application $application
*/
public function __construct(ResourceServer $resourceServer)
public function __construct(ResourceServer $resourceServer, Application $application)
{
$this->resourceServer = $resourceServer;
$this->application = $application;
}

/**
* Set the HTTP request
*
* @param HttpRequest $request
* @return void
* {@inheritDoc}
*/
public function setRequest(HttpRequest $request)
public function isEmpty()
{
$this->request = $request;
$request = $this->getCurrentRequest();

return $request ? $this->resourceServer->getAccessToken($request) === null : true;
}

/**
* {@inheritDoc}
*/
public function isEmpty()
public function read()
{
return $this->resourceServer->getAccessToken($this->request) === null;
$request = $this->getCurrentRequest();

if (! $request) {
return null;
}

$accessToken = $this->resourceServer->getAccessToken($request);

return $accessToken ? $accessToken->getOwner() : null;
}

/**
* {@inheritDoc}
* @return Request|null
*/
public function read()
private function getCurrentRequest()
{
return $this->resourceServer->getAccessToken($this->request)->getOwner();
$request = $this->application->getMvcEvent()->getRequest();

if (! $request instanceof Request || ! $this->resourceServer->isRequestValid($request)) {
return null;
}

return $request;
}
}
14 changes: 5 additions & 9 deletions src/ZfrOAuth2Module/Server/Factory/AccessTokenStorageFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,11 @@ class AccessTokenStorageFactory implements FactoryInterface
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$accessTokenStorage = new AccessTokenStorage($serviceLocator->get('ZfrOAuth2\Server\ResourceServer'));
/* @var $resourceServer \ZfrOAuth2\Server\ResourceServer */
$resourceServer = $serviceLocator->get('ZfrOAuth2\Server\ResourceServer');
/* @var $application \Zend\Mvc\Application */
$application = $serviceLocator->get('Application');

// It only makes sense to set the request if it is HTTP request
$request = $serviceLocator->get('Application')->getRequest();

if ($request instanceof HttpRequest) {
$accessTokenStorage->setRequest($request);
}

return $accessTokenStorage;
return new AccessTokenStorage($resourceServer, $application);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ZfrOAuth2ModuleTest\Server\Authentication\Adapter;

use PHPUnit_Framework_TestCase;
use Zend\Authentication\AuthenticationService;
use Zend\Http\Request as HttpRequest;
use ZfrOAuth2\Server\Entity\AccessToken;
use ZfrOAuth2\Server\Exception\OAuth2Exception;
use ZfrOAuth2Module\Server\Authentication\Storage\AccessTokenStorage;

/**
* @author Marco Pivetta <ocramius@gmail.com>
* @licence MIT
*
* @coversNothing
*/
class AuthenticationFunctionalTest extends PHPUnit_Framework_TestCase
{
/**
* @var \ZfrOAuth2\Server\ResourceServer|\PHPUnit_Framework_MockObject_MockObject
*/
private $resourceServer;

/**
* @var AccessTokenStorage
*/
private $authenticationStorage;

/**
* @var AuthenticationService
*/
private $authenticationService;

/**
* @var \Zend\Mvc\MvcEvent|\PHPUnit_Framework_MockObject_MockObject
*/
private $mvcEvent;

/**
* {@inheritDoc}
*/
protected function setUp()
{
$this->mvcEvent = $this->getMock('Zend\Mvc\MvcEvent');
$application = $this->getMock('Zend\Mvc\Application', [], [], '', false);
$this->resourceServer = $this->getMock('ZfrOAuth2\Server\ResourceServer', [], [], '', false);
$this->authenticationStorage = new AccessTokenStorage($this->resourceServer, $application);
$this->authenticationService = new AuthenticationService($this->authenticationStorage);

$application->expects($this->any())->method('getMvcEvent')->will($this->returnValue($this->mvcEvent));
}

public function testSuccessAuthenticationOnValidToken()
{
$request = new HttpRequest();

$this->mvcEvent->expects($this->any())->method('getRequest')->will($this->returnValue($request));

$token = new AccessToken();
$owner = $this->getMock('ZfrOAuth2\Server\Entity\TokenOwnerInterface');
$token->setOwner($owner);

$this
->resourceServer
->expects($this->atLeastOnce())
->method('isRequestValid')
->with($request)
->will($this->returnValue(true));

$this
->resourceServer
->expects($this->atLeastOnce())
->method('getAccessToken')
->with($request)
->will($this->returnValue($token));


$this->assertTrue($this->authenticationService->hasIdentity());
$this->assertSame($owner, $this->authenticationService->getIdentity());
}

public function testFailAuthenticationOnNoToken()
{
$request = new HttpRequest();

$this->mvcEvent->expects($this->any())->method('getRequest')->will($this->returnValue($request));

$token = new AccessToken();
$owner = $this->getMock('ZfrOAuth2\Server\Entity\TokenOwnerInterface');
$token->setOwner($owner);

$this
->resourceServer
->expects($this->atLeastOnce())
->method('isRequestValid')
->with($request)
->will($this->returnValue(false));

$this->resourceServer->expects($this->never())->method('getAccessToken');

$this->assertFalse($this->authenticationService->hasIdentity());
$this->assertNull($this->authenticationService->getIdentity());
}

public function testFailAuthenticationOnExpiredToken()
{
$request = new HttpRequest();

$this->mvcEvent->expects($this->any())->method('getRequest')->will($this->returnValue($request));

$token = new AccessToken();
$owner = $this->getMock('ZfrOAuth2\Server\Entity\TokenOwnerInterface');
$token->setOwner($owner);

$this
->resourceServer
->expects($this->atLeastOnce())
->method('isRequestValid')
->with($request)
->will($this->returnValue(true));

$this
->resourceServer
->expects($this->atLeastOnce())
->method('getAccessToken')
->with($request)
->will($this->throwException(new OAuth2Exception('Expired token', 123)));

$this->setExpectedException('ZfrOAuth2\Server\Exception\OAuth2Exception', 'Expired token', 123);

$this->authenticationService->getIdentity();
}

public function testFailAuthenticationOnNoRequest()
{
$this->resourceServer->expects($this->never())->method('isRequestValid');
$this->resourceServer->expects($this->never())->method('getAccessToken');

$this->assertFalse($this->authenticationService->hasIdentity());
$this->assertNull($this->authenticationService->getIdentity());
}

public function testFailAuthenticationOnNonHttpRequest()
{
$request = $this->getMock('Zend\Stdlib\RequestInterface');

$this->mvcEvent->expects($this->any())->method('getRequest')->will($this->returnValue($request));

$this->resourceServer->expects($this->never())->method('isRequestValid');
$this->resourceServer->expects($this->never())->method('getAccessToken');

$this->assertFalse($this->authenticationService->hasIdentity());
$this->assertNull($this->authenticationService->getIdentity());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

namespace ZfrOAuth2ModuleTest\Server\Authentication\Storage;

use Zend\Authentication\Result;
use Zend\Http\Request as HttpRequest;
use Zend\Mvc\MvcEvent;
use ZfrOAuth2\Server\Entity\AccessToken;
use ZfrOAuth2Module\Server\Authentication\Storage\AccessTokenStorage;

Expand All @@ -31,39 +31,70 @@
*/
class AccessTokenStorageTest extends \PHPUnit_Framework_TestCase
{
public function testIsConsideredAsEmptyIfNoAccessToken()
{
$resourceServer = $this->getMock('ZfrOAuth2\Server\ResourceServer', [], [], '', false);
$request = new HttpRequest();
/**
* @var \ZfrOAuth2\Server\ResourceServer|\PHPUnit_Framework_MockObject_MockObject
*/
private $resourceServer;

/**
* @var HttpRequest
*/
private $request;

$storage = new AccessTokenStorage($resourceServer);
$storage->setRequest($request);
/**
* @var AccessTokenStorage
*/
private $storage;

$resourceServer->expects($this->once())
->method('getAccessToken')
->with($request)
->will($this->returnValue(null));
/**
* {@inheritDoc}
*/
protected function setUp()
{
$application = $this->getMock('Zend\Mvc\Application', [], [], '', false);
$mvcEvent = new MvcEvent();
$this->resourceServer = $this->getMock('ZfrOAuth2\Server\ResourceServer', [], [], '', false);
$this->request = new HttpRequest();
$this->storage = new AccessTokenStorage($this->resourceServer, $application);

$this->isTrue($storage->isEmpty());
$application->expects($this->any())->method('getMvcEvent')->will($this->returnValue($mvcEvent));
$mvcEvent->setRequest($this->request);
}

public function testReadOwnerFromAccessToken()
public function testIsConsideredAsEmptyIfNoAccessToken()
{
$resourceServer = $this->getMock('ZfrOAuth2\Server\ResourceServer', [], [], '', false);
$request = new HttpRequest();
$this->resourceServer
->expects($this->atLeastOnce())
->method('isRequestValid')
->with($this->request)
->will($this->returnValue(false));

$storage = new AccessTokenStorage($resourceServer);
$storage->setRequest($request);
$this->resourceServer->expects($this->never())->method('getAccessToken');

$this->assertTrue($this->storage->isEmpty());
$this->assertNull($this->storage->read());
}

public function testReadOwnerFromAccessToken()
{
$token = new AccessToken();
$owner = $this->getMock('ZfrOAuth2\Server\Entity\TokenOwnerInterface');

$token->setOwner($owner);

$resourceServer->expects($this->once())
->method('getAccessToken')
->with($request)
->will($this->returnValue($token));
$this->resourceServer
->expects($this->atLeastOnce())
->method('isRequestValid')
->with($this->request)
->will($this->returnValue(true));

$this->resourceServer
->expects($this->atLeastOnce())
->method('getAccessToken')
->with($this->request)
->will($this->returnValue($token));

$this->assertSame($owner, $storage->read());
$this->assertFalse($this->storage->isEmpty());
$this->assertSame($owner, $this->storage->read());
}
}
Loading

0 comments on commit 2ca5bb1

Please sign in to comment.