mirror of
https://github.com/elyby/oauth2-server.git
synced 2024-11-02 00:43:11 +05:30
Merge pull request #178 from ushahidi/resource-server-errors
Resource server errors
This commit is contained in:
commit
4480aa3456
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Insufficient Scope Exception
|
||||
*
|
||||
* @package php-loep/oauth2-server
|
||||
* @author Woody Gilk <woody@shadowhand.me>
|
||||
* @copyright Copyright (c) 2014 PHP League of Extraordinary Packages
|
||||
* @license http://mit-license.org/
|
||||
* @link http://github.com/php-loep/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Exception;
|
||||
|
||||
/**
|
||||
* InsufficientScope Exception
|
||||
*/
|
||||
class InsufficientScopeException extends OAuth2Exception
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Missing Access Token Exception
|
||||
*
|
||||
* @package php-loep/oauth2-server
|
||||
* @author Woody Gilk <woody@shadowhand.me>
|
||||
* @copyright Copyright (c) 2014 PHP League of Extraordinary Packages
|
||||
* @license http://mit-license.org/
|
||||
* @link http://github.com/php-loep/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Exception;
|
||||
|
||||
/**
|
||||
* MissingAccessToken Exception
|
||||
*/
|
||||
class MissingAccessTokenException extends OAuth2Exception
|
||||
{
|
||||
|
||||
}
|
@ -4,7 +4,8 @@
|
||||
*
|
||||
* @package php-loep/oauth2-server
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) 2013 PHP League of Extraordinary Packages
|
||||
* @author Woody Gilk <woody@shadowhand.me>
|
||||
* @copyright Copyright (c) 2013-2014 PHP League of Extraordinary Packages
|
||||
* @license http://mit-license.org/
|
||||
* @link http://github.com/php-loep/oauth2-server
|
||||
*/
|
||||
@ -75,6 +76,117 @@ class Resource
|
||||
*/
|
||||
protected $clientId = null;
|
||||
|
||||
/**
|
||||
* Exception error codes
|
||||
* @var array
|
||||
*/
|
||||
protected static $exceptionCodes = array(
|
||||
0 => 'invalid_request',
|
||||
1 => 'invalid_token',
|
||||
2 => 'insufficient_scope',
|
||||
);
|
||||
|
||||
/**
|
||||
* Exception error messages
|
||||
* @var array
|
||||
*/
|
||||
protected static $exceptionMessages = array(
|
||||
'invalid_request' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "%s" parameter.',
|
||||
'invalid_token' => 'The access token provided is expired, revoked, malformed, or invalid for other reasons.',
|
||||
'insufficient_scope' => 'The request requires higher privileges than provided by the access token. Required scopes are: %s.',
|
||||
);
|
||||
|
||||
/**
|
||||
* Exception error HTTP status codes
|
||||
* @var array
|
||||
*
|
||||
* RFC 6750, section 3.1:
|
||||
* When a request fails, the resource server responds using the
|
||||
* appropriate HTTP status code (typically, 400, 401, 403, or 405) and
|
||||
* includes one of the following error codes in the response:
|
||||
*/
|
||||
protected static $exceptionHttpStatusCodes = array(
|
||||
'invalid_request' => 400,
|
||||
'invalid_token' => 401,
|
||||
'insufficient_scope' => 403,
|
||||
);
|
||||
|
||||
/**
|
||||
* Get an exception message
|
||||
*
|
||||
* @param string $error The error message key
|
||||
* @return string The error message
|
||||
*/
|
||||
public static function getExceptionMessage($error = '')
|
||||
{
|
||||
return self::$exceptionMessages[$error];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an exception code
|
||||
*
|
||||
* @param integer $code The exception code
|
||||
* @return string The exception code type
|
||||
*/
|
||||
public static function getExceptionType($code = 0)
|
||||
{
|
||||
return self::$exceptionCodes[$code];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all headers that have to be send with the error response
|
||||
*
|
||||
* @param string $error The error message key
|
||||
* @return array Array with header values
|
||||
*/
|
||||
public static function getExceptionHttpHeaders($error)
|
||||
{
|
||||
$headers = array();
|
||||
switch (self::$exceptionHttpStatusCodes[$error]) {
|
||||
case 401:
|
||||
$headers[] = 'HTTP/1.1 401 Unauthorized';
|
||||
break;
|
||||
case 403:
|
||||
$headers[] = 'HTTP/1.1 403 Forbidden';
|
||||
break;
|
||||
case 400:
|
||||
default:
|
||||
$headers[] = 'HTTP/1.1 400 Bad Request';
|
||||
}
|
||||
|
||||
// Add "WWW-Authenticate" header
|
||||
//
|
||||
// RFC 6749, section 5.2.:
|
||||
// "If the client attempted to authenticate via the 'Authorization'
|
||||
// request header field, the authorization server MUST
|
||||
// respond with an HTTP 401 (Unauthorized) status code and
|
||||
// include the "WWW-Authenticate" response header field
|
||||
// matching the authentication scheme used by the client.
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($error === 'insufficient_scope') {
|
||||
$authScheme = null;
|
||||
$request = new Request();
|
||||
if ($request->server('PHP_AUTH_USER') !== null) {
|
||||
$authScheme = 'Basic';
|
||||
} else {
|
||||
$authHeader = $request->header('Authorization');
|
||||
if ($authHeader !== null) {
|
||||
if (strpos($authHeader, 'Bearer') === 0) {
|
||||
$authScheme = 'Bearer';
|
||||
} elseif (strpos($authHeader, 'Basic') === 0) {
|
||||
$authScheme = 'Basic';
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($authScheme !== null) {
|
||||
$headers[] = 'WWW-Authenticate: '.$authScheme.' realm=""';
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the Resource
|
||||
*
|
||||
@ -186,7 +298,7 @@ class Resource
|
||||
$result = $this->storages['session']->validateAccessToken($accessToken);
|
||||
|
||||
if (! $result) {
|
||||
throw new Exception\InvalidAccessTokenException('Access token is not valid');
|
||||
throw new Exception\InvalidAccessTokenException(self::$exceptionMessages['invalid_token'], 1);
|
||||
}
|
||||
|
||||
$this->accessToken = $accessToken;
|
||||
@ -216,25 +328,26 @@ class Resource
|
||||
* Checks if the presented access token has the given scope(s).
|
||||
*
|
||||
* @param array|string An array of scopes or a single scope as a string
|
||||
* @param bool If scopes are required, missing scope will trigger an exception
|
||||
* @throws Exception\InsufficientScopeException Thrown if the any of the given scopes are not in the session
|
||||
* @return bool Returns bool if all scopes are found, false if any fail
|
||||
*/
|
||||
public function hasScope($scopes)
|
||||
public function hasScope($scopes, $required = false)
|
||||
{
|
||||
if (is_string($scopes)) {
|
||||
if (in_array($scopes, $this->sessionScopes)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} elseif (is_array($scopes)) {
|
||||
foreach ($scopes as $scope) {
|
||||
if (! in_array($scope, $this->sessionScopes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
if (!is_array($scopes)) {
|
||||
$scopes = array($scopes);
|
||||
}
|
||||
|
||||
return false;
|
||||
$missing = array_diff($scopes, $this->sessionScopes);
|
||||
|
||||
if ($missing) {
|
||||
if ($required) {
|
||||
$missing = implode(', ', $missing);
|
||||
throw new Exception\InsufficientScopeException(sprintf(self::$exceptionMessages['insufficient_scope'], $missing), 3);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -274,7 +387,7 @@ class Resource
|
||||
}
|
||||
|
||||
if (empty($accessToken)) {
|
||||
throw new Exception\InvalidAccessTokenException('Access token is missing');
|
||||
throw new Exception\MissingAccessTokenException(self::$exceptionMessages['invalid_request'], 0);
|
||||
}
|
||||
|
||||
return $accessToken;
|
||||
|
@ -16,6 +16,32 @@ class Resource_Server_test extends PHPUnit_Framework_TestCase
|
||||
return new League\OAuth2\Server\Resource($this->session);
|
||||
}
|
||||
|
||||
public function test_getExceptionMessage()
|
||||
{
|
||||
$m = League\OAuth2\Server\Resource::getExceptionMessage('invalid_request');
|
||||
|
||||
$reflector = new ReflectionClass($this->returnDefault());
|
||||
$exceptionMessages = $reflector->getProperty('exceptionMessages');
|
||||
$exceptionMessages->setAccessible(true);
|
||||
$v = $exceptionMessages->getValue();
|
||||
|
||||
$this->assertEquals($v['invalid_request'], $m);
|
||||
}
|
||||
|
||||
public function test_getExceptionCode()
|
||||
{
|
||||
$this->assertEquals('invalid_request', League\OAuth2\Server\Resource::getExceptionType(0));
|
||||
$this->assertEquals('invalid_token', League\OAuth2\Server\Resource::getExceptionType(1));
|
||||
$this->assertEquals('insufficient_scope', League\OAuth2\Server\Resource::getExceptionType(2));
|
||||
}
|
||||
|
||||
public function test_getExceptionHttpHeaders()
|
||||
{
|
||||
$this->assertEquals(array('HTTP/1.1 400 Bad Request'), League\OAuth2\Server\Resource::getExceptionHttpHeaders('invalid_request'));
|
||||
$this->assertEquals(array('HTTP/1.1 401 Unauthorized'), League\OAuth2\Server\Resource::getExceptionHttpHeaders('invalid_token'));
|
||||
$this->assertContains('HTTP/1.1 403 Forbidden', League\OAuth2\Server\Resource::getExceptionHttpHeaders('insufficient_scope'));
|
||||
}
|
||||
|
||||
public function test_setRequest()
|
||||
{
|
||||
$s = $this->returnDefault();
|
||||
@ -66,7 +92,7 @@ class Resource_Server_test extends PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException League\OAuth2\Server\Exception\InvalidAccessTokenException
|
||||
* @expectedException League\OAuth2\Server\Exception\MissingAccessTokenException
|
||||
*/
|
||||
public function test_determineAccessToken_missingToken()
|
||||
{
|
||||
@ -84,7 +110,7 @@ class Resource_Server_test extends PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException League\OAuth2\Server\Exception\InvalidAccessTokenException
|
||||
* @expectedException League\OAuth2\Server\Exception\MissingAccessTokenException
|
||||
*/
|
||||
public function test_determineAccessToken_brokenCurlRequest()
|
||||
{
|
||||
@ -166,6 +192,39 @@ class Resource_Server_test extends PHPUnit_Framework_TestCase
|
||||
$this->assertEquals('abcdef', $result);
|
||||
}
|
||||
|
||||
public function test_hasScope_isRequired()
|
||||
{
|
||||
$s = $this->returnDefault();
|
||||
|
||||
$reflector = new ReflectionClass($s);
|
||||
$param = $reflector->getProperty('sessionScopes');
|
||||
$param->setAccessible(true);
|
||||
$param->setValue($s, array(
|
||||
'a', 'b', 'c'
|
||||
));
|
||||
|
||||
$result = $s->hasScope(array('a', 'b'), true);
|
||||
|
||||
$this->assertEquals(true, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException League\OAuth2\Server\Exception\InsufficientScopeException
|
||||
*/
|
||||
public function test_hasScope_isRequiredFailure()
|
||||
{
|
||||
$s = $this->returnDefault();
|
||||
|
||||
$reflector = new ReflectionClass($s);
|
||||
$param = $reflector->getProperty('sessionScopes');
|
||||
$param->setAccessible(true);
|
||||
$param->setValue($s, array(
|
||||
'a', 'b', 'c'
|
||||
));
|
||||
|
||||
$s->hasScope('d', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException League\OAuth2\Server\Exception\InvalidAccessTokenException
|
||||
*/
|
||||
@ -216,11 +275,10 @@ class Resource_Server_test extends PHPUnit_Framework_TestCase
|
||||
$this->assertEquals('user', $s->getOwnerType());
|
||||
$this->assertEquals('abcdef', $s->getAccessToken());
|
||||
$this->assertEquals('testapp', $s->getClientId());
|
||||
$this->assertTrue($s->hasScope('foo'));
|
||||
$this->assertTrue($s->hasScope('bar'));
|
||||
$this->assertTrue($s->hasScope(array('foo', 'bar')));
|
||||
$this->assertFalse($s->hasScope(array('foobar')));
|
||||
$this->assertFalse($s->hasScope('foobar'));
|
||||
$this->assertFalse($s->hasScope(new StdClass));
|
||||
$this->assertTrue($s->hasScope('foo'));
|
||||
$this->assertTrue($s->hasScope('bar'));
|
||||
$this->assertTrue($s->hasScope(array('foo', 'bar')));
|
||||
$this->assertFalse($s->hasScope(array('foobar')));
|
||||
$this->assertFalse($s->hasScope('foobar'));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user