diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..de1a9ba2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/vendor/
+/composer.lock
+/docs/build/
\ No newline at end of file
diff --git a/build/phpunit.xml b/build/phpunit.xml
index 4181c278..3a5b9f99 100644
--- a/build/phpunit.xml
+++ b/build/phpunit.xml
@@ -8,9 +8,12 @@
stopOnIncomplete="false"
stopOnSkipped="false">
-
+
../tests/authentication
+
+ ../tests/resource
+
@@ -19,11 +22,8 @@
-
+
-
+
\ No newline at end of file
diff --git a/src/Oauth2/Authentication/Server.php b/src/Oauth2/Authentication/Server.php
index 1e6ee1f5..733b51e3 100644
--- a/src/Oauth2/Authentication/Server.php
+++ b/src/Oauth2/Authentication/Server.php
@@ -513,6 +513,6 @@ class Server
unset($args[0]);
$params = array_values($args);
- return call_user_func_array(array($this->db, $method), $args);
+ return call_user_func_array(array($this->db, $method), $params);
}
}
diff --git a/src/Oauth2/Resource/Database.php b/src/Oauth2/Resource/Database.php
index c39bb471..9c5d1b44 100644
--- a/src/Oauth2/Resource/Database.php
+++ b/src/Oauth2/Resource/Database.php
@@ -4,4 +4,56 @@ namespace Oauth2\Resource;
interface Database
{
+ /**
+ * Validate an access token and return the session details.
+ *
+ * Database query:
+ *
+ *
+ * SELECT id, owner_type, owner_id FROM oauth_sessions WHERE access_token =
+ * $accessToken AND stage = 'granted' AND
+ * access_token_expires > UNIX_TIMESTAMP(now())
+ *
+ *
+ * Response:
+ *
+ *
+ * Array
+ * (
+ * [id] => (int) The session ID
+ * [owner_type] => (string) The session owner type
+ * [owner_id] => (string) The session owner's ID
+ * )
+ *
+ *
+ * @param string $accessToken The access token
+ * @return array|bool Return an array on success or false on failure
+ */
+ public function validateAccessToken($accessToken);
+
+ /**
+ * Returns the scopes that the session is authorised with.
+ *
+ * Database query:
+ *
+ *
+ * SELECT scope FROM oauth_session_scopes WHERE access_token =
+ * '291dca1c74900f5f252de351e0105aa3fc91b90b'
+ *
+ *
+ * Response:
+ *
+ *
+ * Array
+ * (
+ * [0] => (string) A scope
+ * [1] => (string) Another scope
+ * ...
+ * )
+ *
+ *
+ * @param int $sessionId The session ID
+ * @return array A list of scopes
+ */
+ public function sessionScopes($sessionId);
}
\ No newline at end of file
diff --git a/src/Oauth2/Resource/Server.php b/src/Oauth2/Resource/Server.php
index 15d62fa1..ab4626c3 100644
--- a/src/Oauth2/Resource/Server.php
+++ b/src/Oauth2/Resource/Server.php
@@ -2,7 +2,223 @@
namespace Oauth2\Resource;
+class OAuthResourceServerException extends \Exception
+{
+
+}
+
class Server
{
+ /**
+ * Reference to the database abstractor
+ * @var object
+ */
+ private $_db = null;
+
+ /**
+ * The access token.
+ * @access private
+ */
+ private $_accessToken = null;
+
+ /**
+ * The scopes the access token has access to.
+ * @access private
+ */
+ private $_scopes = array();
+
+ /**
+ * The type of owner of the access token.
+ * @access private
+ */
+ private $_type = null;
+
+ /**
+ * The ID of the owner of the access token.
+ * @access private
+ */
+ private $_typeId = null;
+
+ /**
+ * Server configuration
+ * @var array
+ */
+ private $_config = array(
+ 'token_key' => 'oauth_token'
+ );
+
+ /**
+ * Error codes.
+ *
+ * To provide i8ln errors just overwrite the keys
+ *
+ * @var array
+ */
+ public $errors = array(
+ 'missing_access_token' => 'An access token was not presented with the request',
+ 'invalid_access_token' => 'The access token is not registered with the resource server'
+ );
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @return void
+ */
+ public function __construct($options = null)
+ {
+ if ($options !== null) {
+ $this->config = array_merge($this->config, $options);
+ }
+ }
+
+ /**
+ * Magic method to test if access token represents a particular owner type
+ * @param string $method The method name
+ * @param mixed $arguements The method arguements
+ * @return bool If method is valid, and access token is owned by the requested party then true,
+ */
+ public function __call($method, $arguements = null)
+ {
+ if (substr($method, 0, 2) === 'is') {
+
+ if ($this->_type === strtolower(substr($method, 2))) {
+ return $this->_typeId;
+ }
+
+ return false;
+ }
+
+ trigger_error('Call to undefined function ' . $method . '()');
+ }
+
+ /**
+ * Register a database abstrator class
+ *
+ * @access public
+ * @param object $db A class that implements OAuth2ServerDatabase
+ * @return void
+ */
+ public function registerDbAbstractor($db)
+ {
+ $this->_db = $db;
+ }
+ /**
+ * Init function
+ *
+ * @access public
+ * @return void
+ */
+ public function init()
+ {
+ $accessToken = null;
+
+ // Try and get the access token via an access_token or oauth_token parameter
+ switch ($server['REQUEST_METHOD'])
+ {
+ case 'POST':
+ $accessToken = isset($_POST[$this->_config['token_key']]) ? $_POST[$this->_config['token_key']] : null;
+ break;
+
+ default:
+ $accessToken = isset($_GET[$this->_config['token_key']]) ? $_GET[$this->_config['token_key']] : null;
+ break;
+ }
+
+ // Try and get an access token from the auth header
+ $headers = getallheaders();
+ if (isset($headers['Authorization'])) {
+
+ $rawToken = trim(str_replace('Bearer', '', $headers['Authorization']));
+ if ( ! empty($rawToken))
+ {
+ $accessToken = base64_decode($rawToken);
+ }
+ }
+
+ if ($accessToken) {
+
+ $result = $this->_dbCall('validateAccessToken', array($accessToken));
+
+ if ($result === false)
+ {
+ throw new OAuthResourceServerException($this->errors['invalid_access_token']);
+ }
+
+ else
+ {
+ $this->_accessToken = $accessToken;
+ $this->_type = $result['owner_type'];
+ $this->_typeId = $result['owner_id'];
+
+ // Get the scopes
+ $this->_scopes = $this->_dbCall('sessionScopes', array($result['id']));
+ }
+
+ } else {
+
+ throw new OAuthResourceServerException($this->errors['missing_access_token']);
+
+ }
+ }
+
+ /**
+ * Test if the access token has a specific scope
+ *
+ * @param mixed $scopes Scope(s) to check
+ *
+ * @access public
+ * @return string|bool
+ */
+ public function hasScope($scopes)
+ {
+ if (is_string($scopes))
+ {
+ if (in_array($scopes, $this->_scopes))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ elseif (is_array($scopes))
+ {
+ foreach ($scopes as $scope)
+ {
+ if ( ! in_array($scope, $this->_scopes))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Call database methods from the abstractor
+ *
+ * @return mixed The query result
+ */
+ private function _dbCall()
+ {
+ if ($this->_db === null) {
+ throw new OAuthResourceServerException('No registered database abstractor');
+ }
+
+ if ( ! $this->_db instanceof Database) {
+ throw new OAuthResourceServerException('Registered database abstractor is not an instance of Oauth2\Resource\Database');
+ }
+
+ $args = func_get_args();
+ $method = $args[0];
+ unset($args[0]);
+ $params = array_values($args);
+
+ return call_user_func_array(array($this->_db, $method), $params);
+ }
}
\ No newline at end of file
diff --git a/tests/resource/database_mock.php b/tests/resource/database_mock.php
new file mode 100644
index 00000000..15f9dc28
--- /dev/null
+++ b/tests/resource/database_mock.php
@@ -0,0 +1,29 @@
+ array(
+ 'id' => 1,
+ 'owner_type' => 'user',
+ 'owner_id' => 123
+ ));
+
+ private $sessionScopes = array(
+ 1 => array(
+ 'foo',
+ 'bar'
+ )
+ );
+
+ public function validateAccessToken($accessToken)
+ {
+ return (isset($this->accessTokens[$accessToken])) ? $this->accessTokens[$accessToken] : false;
+ }
+
+ public function sessionScopes($sessionId)
+ {
+ return (isset($this->sessionScopes[$sessionId])) ? $this->sessionScopes[$sessionId] : array();
+ }
+}
\ No newline at end of file
diff --git a/tests/resource/server_test.php b/tests/resource/server_test.php
new file mode 100644
index 00000000..1d91536e
--- /dev/null
+++ b/tests/resource/server_test.php
@@ -0,0 +1,77 @@
+server = new Oauth2\Resource\Server();
+ $this->db = new ResourceDB();
+
+ $this->server->registerDbAbstractor($this->db);
+ }
+
+ function test_init_POST()
+ {
+ $_POST['oauth_token'] = 'test12345';
+
+ $this->server->init();
+
+ $this->assertEquals($this->server->_accessToken, $_POST['oauth_token']);
+ $this->assertEquals($this->server->_type, 'user');
+ $this->assertEquals($this->server->_typeId, 123);
+ $this->assertEquals($this->server->_scopes, array('foo', 'bar'));
+ }
+
+ function test_init_GET()
+ {
+ $_GET['oauth_token'] = 'test12345';
+
+ $this->server->init();
+
+ $this->assertEquals($this->server->_accessToken, $_GET['oauth_token']);
+ $this->assertEquals($this->server->_type, 'user');
+ $this->assertEquals($this->server->_typeId, 123);
+ $this->assertEquals($this->server->_scopes, array('foo', 'bar'));
+ }
+
+ function test_init_header()
+ {
+ // Test with authorisation header
+ }
+
+ /**
+ * @exception OAuthResourceServerException
+ */
+ function test_init_wrongToken()
+ {
+ $_POST['access_token'] = 'test12345';
+
+ $this->server->init();
+ }
+
+ function test_hasScope()
+ {
+ $_POST['oauth_token'] = 'test12345';
+
+ $this->server->init();
+
+ $this->assertEquals(true, $this->server->hasScope('foo'));
+ $this->assertEquals(true, $this->server->hasScope('bar'));
+ $this->assertEquals(true, $this->server->hasScope(array('foo', 'bar')));
+
+ $this->assertEquals(false, $this->server->hasScope('foobar'));
+ $this->assertEquals(false, $this->server->hasScope(array('foobar')));
+ }
+
+ function test___call()
+ {
+ $_POST['oauth_token'] = 'test12345';
+
+ $this->server->init();
+
+ $this->assertEquals(123, $this->server->isUser());
+ $this->assertEquals(false, $this->server->isMachine());
+ }
+
+}
\ No newline at end of file