}
if ($parameters != NULL) {
- // ensure the required flag is set correctly in default case
+ // ensure the required flag is set correctly in default case for each parameter
foreach ($parameters as $key => $value) {
+ // check if 'required' was specified - if not, make it true
if (!array_key_exists('required', $value)) {
$parameters[$key]['required'] = true;
}
// check that the parameters were registered correctly and all required ones are there
foreach ($API_METHODS[$method]['parameters'] as $key => $value) {
- // must be array to describe parameter in expose and type must be defined
+ // this tests the expose structure: must be array to describe parameter and type must be defined
if (!is_array($value) || !isset($value['type'])) {
throw new APIException(sprintf(elgg_echo('APIException:InvalidParameter'), $key, $method));
}
// Check that the variable is present in the request if required
- $is_param_required = !isset($value['required']) || $value['required'];
- if ($is_param_required && !array_key_exists($key, $parameters)) {
+ if ($value['required'] && !array_key_exists($key, $parameters)) {
throw new APIException(sprintf(elgg_echo('APIException:MissingParameterInMethod'), $key, $method));
}
}
// check that it is active
$api_user = get_api_user($CONFIG->site_id, $api_key);
if (!$api_user) {
- throw new APIException(elgg_echo('APIException:MissingAPIKey'));
+ // key is not active or does not exist
+ throw new APIException(elgg_echo('APIException:BadAPIKey'));
}
+ // can be used for keeping stats
+ // plugin can also return false to fail this authentication method
return trigger_plugin_hook('api_key', 'use', $api_key, true);
}
// API key functions /////////////////////////////////////////////////////////////////////
+/**
+ * Generate a new API user for a site, returning a new keypair on success.
+ *
+ * @param int $site_guid The GUID of the site. (default is current site)
+ */
+function create_api_user($site_guid) {
+ global $CONFIG;
+
+ if (!isset($site_guid)) {
+ $site_guid = $CONFIG->site_id;
+ }
+
+ $site_guid = (int)$site_guid;
+
+ $public = sha1(rand().$site_guid.microtime());
+ $secret = sha1(rand().$site_guid.microtime().$public);
+
+ $insert = insert_data("INSERT into {$CONFIG->dbprefix}api_users
+ (site_guid, api_key, secret) values
+ ($site_guid, '$public', '$secret')");
+
+ if ($insert) {
+ return get_api_user($site_guid, $public);
+ }
+
+ return false;
+}
+
/**
* Find an API User's details based on the provided public api key. These users are not users in the traditional sense.
*
return false;
}
-/**
- * Generate a new API user for a site, returning a new keypair on success.
- *
- * @param int $site_guid The GUID of the site.
- */
-function create_api_user($site_guid) {
- global $CONFIG;
-
- $site_guid = (int)$site_guid;
-
- $public = sha1(rand().$site_guid.microtime());
- $secret = sha1(rand().$site_guid.microtime().$public);
-
- $insert = insert_data("INSERT into {$CONFIG->dbprefix}api_users
- (site_guid, api_key, secret) values
- ($site_guid, '$public', '$secret')");
-
- if ($insert) {
- return get_api_user($site_guid, $public);
- }
-
- return false;
-}
// User Authorization functions ////////////////////////////////////////////////////////////////
$token = get_input('auth_token');
- $validated_userid = validate_user_token($CONFIG->site_id, $token);
+ $validated_userid = validate_user_token($token);
if ($validated_userid) {
$u = get_entity($validated_userid);
* Obtain a token for a user.
*
* @param string $username The username
- * @param string $password The password
+ * @param int $expire minutes until token expires (default is 60 minutes)
*/
-function obtain_user_token($username, $password) {
+function create_user_token($username, $expire = 60) {
global $CONFIG;
- $site = $CONFIG->site_id;
+ $site_guid = $CONFIG->site_id;
$user = get_user_by_username($username);
$time = time();
- $time += 60*60; // token is good for one hour
- $token = md5(rand(). microtime() . $username . $password . $time . $site);
+ $time += 60 * $expire;
+ $token = md5(rand(). microtime() . $username . $time . $site_guid);
if (!$user) {
return false;
if (insert_data("INSERT into {$CONFIG->dbprefix}users_apisessions
(user_guid, site_guid, token, expires) values
- ({$user->guid}, $site, '$token', '$time') on duplicate key update token='$token', expires='$time'")) {
+ ({$user->guid}, $site_guid, '$token', '$time') on duplicate key update token='$token', expires='$time'")) {
return $token;
}
* A token registered with one site can not be used from a different apikey(site), so be aware of this
* during development.
*
- * @param int $site The ID of the site
* @param string $token The Token.
+ * @param int $site_guid The ID of the site (default is current site)
* @return mixed The user id attached to the token or false.
*/
-function validate_user_token($site, $token) {
+function validate_user_token($token, $site_guid) {
global $CONFIG;
- $site = (int)$site;
- $token = sanitise_string($token);
-
- if (!$site) {
- throw new ConfigurationException(elgg_echo('ConfigurationException:NoSiteID'));
+ if (!isset($site_guid)) {
+ $site_guid = $CONFIG->site_id;
}
+
+ $site_guid = (int)$site_guid;
+ $token = sanitise_string($token);
$time = time();
$user = get_data_row("SELECT * from {$CONFIG->dbprefix}users_apisessions
- where token='$token' and site_guid=$site and $time < expires");
+ where token='$token' and site_guid=$site_guid and $time < expires");
if ($user) {
return $user->user_guid;
return false;
}
+/**
+ * Remove user token
+ *
+ * @param string $token
+ * @param int $site_guid The ID of the site (default is current site)
+ * @return bool
+ */
+function remove_user_token($token, $site_guid) {
+ global $CONFIG;
+
+ if (!isset($site_guid)) {
+ $site_guid = $CONFIG->site_id;
+ }
+
+ $site_guid = (int)$site_guid;
+ $token = sanitise_string($token);
+
+ return delete_data("DELETE from {$CONFIG->dbprefix}users_apisessions
+ where site_guid=$site_guid and token='$token'");
+}
+
+/**
+ * Remove expired tokens
+ *
+ * @return bool
+ */
+function remove_expired_user_tokens() {
+ global $CONFIG;
+
+ $site_guid = $CONFIG->site_id;
+
+ $time = time();
+
+ return delete_data("DELETE from {$CONFIG->dbprefix}users_apisessions
+ where site_guid=$site_guid and expires < $time");
+}
+
// Client api functions ///////////////////////////////////////////////////////////////////
/**
*/
function auth_gettoken($username, $password) {
if (authenticate($username, $password)) {
- $token = obtain_user_token($username, $password);
+ $token = create_user_token($username);
if ($token) {
return $token;
}
\r
// expose_function\r
public function testExposeFunctionNoMethod() {\r
- \r
- $this->expectException('InvalidParameterException');\r
- expose_function();\r
+ try {\r
+ expose_function();\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'InvalidParameterException');\r
+ $this->assertIdentical($e->getMessage(), elgg_echo('InvalidParameterException:APIMethodOrFunctionNotSet'));\r
+ }\r
}\r
\r
public function testExposeFunctionNoFunction() {\r
- $this->expectException('InvalidParameterException');\r
- expose_function('test');\r
+ try {\r
+ expose_function('test');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'InvalidParameterException');\r
+ $this->assertIdentical($e->getMessage(), elgg_echo('InvalidParameterException:APIMethodOrFunctionNotSet'));\r
+ }\r
}\r
\r
public function testExposeFunctionBadParameters() {\r
- $this->expectException('InvalidParameterException');\r
- expose_function('test', 'test', 'BAD');\r
+ try {\r
+ expose_function('test', 'test', 'BAD');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'InvalidParameterException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('InvalidParameterException:APIParametersArrayStructure'), 'test'));\r
+ }\r
}\r
\r
- public function testExposeFunctionParametersNotArray() {\r
- $this->expectException('InvalidParameterException');\r
- expose_function('test', 'test', array('param1' => 'string'));\r
+ public function testExposeFunctionParametersBadArray() {\r
+ try {\r
+ expose_function('test', 'test', array('param1' => 'string'));\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'InvalidParameterException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('InvalidParameterException:APIParametersArrayStructure'), 'test'));\r
+ }\r
}\r
\r
public function testExposeFunctionBadHttpMethod() {\r
- $this->expectException('InvalidParameterException');\r
- expose_function('test', 'test', null, '', 'BAD');\r
+ try {\r
+ expose_function('test', 'test', null, '', 'BAD');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'InvalidParameterException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('InvalidParameterException:UnrecognisedHttpMethod'), 'BAD', 'test'));\r
+ }\r
}\r
\r
public function testExposeFunctionSuccess() {\r
global $API_METHODS;\r
- $parameters = array('param1' => array('type' => 'int', 'required' => true));\r
+ // this is a general test but also tests specifically for setting 'required' correctly\r
+ $parameters = array('param1' => array('type' => 'int', 'required' => true), \r
+ 'param2' => array('type' => 'bool'),\r
+ 'param3' => array('type' => 'string', 'required' => false), );\r
+ \r
+ $this->assertTrue(expose_function('test', 'foo', $parameters));\r
+ \r
+ $parameters = array('param1' => array('type' => 'int', 'required' => true), \r
+ 'param2' => array('type' => 'bool', 'required' => true),\r
+ 'param3' => array('type' => 'string', 'required' => false), );\r
$method['function'] = 'foo';\r
$method['parameters'] = $parameters;\r
$method['call_method'] = 'GET'; \r
$method['require_api_auth'] = false;\r
$method['require_user_auth'] = false;\r
\r
- $this->assertTrue(expose_function('test', 'foo', $parameters));\r
$this->assertIdentical($method, $API_METHODS['test']);\r
}\r
\r
} \r
\r
// authenticate_method\r
- public function testApiMethodNotImplemented() {\r
- global $CONFIG;\r
- \r
- $results = send_api_get_call($CONFIG->wwwroot . 'pg/api/rest/json/', array('method' => 'bad.method'));\r
- $obj = json_decode($results);\r
- $this->assertIdentical(sprintf(elgg_echo('APIException:MethodCallNotImplemented'), 'bad.method'), $obj->api[0]->message);\r
+ public function testAuthenticateMethodNotImplemented() {\r
+ try {\r
+ authenticate_method('BAD');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('APIException:MethodCallNotImplemented'), 'BAD'));\r
+ } \r
}\r
-\r
- public function testAuthenticateForApi() {\r
- $this->registerFunction(true, false);\r
- \r
- $this->expectException('APIException');\r
- authenticate_method('test');\r
+ \r
+ public function testAuthenticateMethodApiAuth() {\r
+ $this->registerFunction(true);\r
+ try {\r
+ authenticate_method('test');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), elgg_echo('APIException:APIAuthenticationFailed'));\r
+ } \r
}\r
-\r
- public function testAuthenticateForUser() {\r
+ \r
+ public function testAuthenticateMethodUserAuth() {\r
$this->registerFunction(false, true);\r
- \r
- $this->expectException('APIException');\r
- authenticate_method('test');\r
+ try {\r
+ authenticate_method('test');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), elgg_echo('APIException:UserAuthenticationFailed'));\r
+ } \r
}\r
\r
public function testAuthenticateMethod() {\r
$this->assertTrue(authenticate_method('test'));\r
}\r
\r
-// api_authenticate\r
- public function testApiAuthenticate() {\r
- $this->registerFunction(true, false);\r
- \r
- $this->assertFalse(api_authenticate());\r
- }\r
- \r
// execute_method\r
+ public function testExecuteMethodNotImplemented() {\r
+ try {\r
+ execute_method('BAD');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('APIException:MethodCallNotImplemented'), 'BAD'));\r
+ } \r
+ }\r
+\r
public function testExecuteMethodNonCallable() {\r
expose_function('test', 'foo');\r
\r
- $this->expectException('ApiException');\r
- execute_method('test');\r
+ try {\r
+ execute_method('test');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('APIException:FunctionDoesNotExist'), 'test'));\r
+ } \r
}\r
\r
public function testExecuteMethodWrongMethod() {\r
$this->registerFunction();\r
\r
- // get when it should be a post\r
- $this->expectException('CallException');\r
- execute_method('test');\r
+ try {\r
+ // GET when it should be a POST\r
+ execute_method('test');\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'CallException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('CallException:InvalidCallMethod'), 'test', 'POST'));\r
+ } \r
+ }\r
+\r
+// verify parameters\r
+ public function testVerifyParametersTypeNotSet() {\r
+ $params = array('param1' => array('required' => true));\r
+ expose_function('test', 'elgg_echo', $params);\r
+ \r
+ try {\r
+ verify_parameters('test', array());\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('APIException:InvalidParameter'), 'param1', 'test'));\r
+ } \r
+ }\r
+ \r
+ public function testVerifyParametersMissing() {\r
+ $params = array('param1' => array('type' => 'int', 'required' => true));\r
+ expose_function('test', 'elgg_echo', $params);\r
+ \r
+ try {\r
+ verify_parameters('test', array());\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), sprintf(elgg_echo('APIException:MissingParameterInMethod'), 'param1', 'test'));\r
+ } \r
}\r
\r
public function testVerifyParameters() {\r
\r
$parameters = array('param1' => 0);\r
$this->assertTrue(verify_parameters('test', $parameters));\r
- \r
- $parameters = array('param2' => true);\r
- $this->expectException('APIException');\r
- $this->assertTrue(verify_parameters('test', $parameters));\r
}\r
\r
- public function testserialise_parameters() {\r
+ public function testSerialiseParameters() {\r
\r
// int and bool\r
$this->registerFunction();\r
$s = serialise_parameters('test', $parameters);\r
}\r
\r
+// api key methods\r
+ public function testApiAuthenticate() {\r
+ $this->assertFalse(api_authenticate());\r
+ }\r
+ \r
+ public function testApiAuthKeyNoKey() {\r
+ try {\r
+ api_auth_key();\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), elgg_echo('APIException:MissingAPIKey'));\r
+ }\r
+ }\r
+\r
+ public function testApiAuthKeyBadKey() {\r
+ global $CONFIG;\r
+ \r
+ $CONFIG->input['api_key'] = 'BAD';\r
+ try {\r
+ api_auth_key();\r
+ $this->assertTrue(FALSE);\r
+ } catch (Exception $e) {\r
+ $this->assertIsA($e, 'APIException');\r
+ $this->assertIdentical($e->getMessage(), elgg_echo('APIException:BadAPIKey'));\r
+ }\r
+ }\r
+ \r
protected function registerFunction($api_auth = false, $user_auth = false, $params = null) {\r
$parameters = array('param1' => array('type' => 'int', 'required' => true),\r
'param2' => array('type' => 'bool', 'required' => false), );\r