aboutsummaryrefslogtreecommitdiff
path: root/engine/lib
diff options
context:
space:
mode:
Diffstat (limited to 'engine/lib')
-rw-r--r--engine/lib/api.php2298
1 files changed, 1167 insertions, 1131 deletions
diff --git a/engine/lib/api.php b/engine/lib/api.php
index 2ec36388b..53af1af67 100644
--- a/engine/lib/api.php
+++ b/engine/lib/api.php
@@ -1,1227 +1,1263 @@
<?php
+/**
+ * Elgg API
+ * Functions and objects which make up the API engine.
+ *
+ * @package Elgg
+ * @subpackage Core
+ * @author Curverider Ltd <info@elgg.com>
+ * @link http://elgg.org/
+ */
+
+// Result classes /////////////////////////////////////////////////////////////////////////
+
+/**
+ * GenericResult Result superclass.
+ *
+ * @author Curverider Ltd <info@elgg.com>
+ * @package Elgg
+ * @subpackage Core
+ */
+abstract class GenericResult {
/**
- * Elgg API
- * Functions and objects which make up the API engine.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * The status of the result.
+ * @var int
*/
+ private $status_code;
- // Result classes /////////////////////////////////////////////////////////////////////////
+ /**
+ * Message returned along with the status which is almost always an error message.
+ * This must be human readable, understandable and localised.
+ * @var string
+ */
+ private $message;
/**
- * GenericResult Result superclass.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
+ * Result store.
+ * Attach result specific informaton here.
+ *
+ * @var mixed. Should probably be an object of some sort.
*/
- abstract class GenericResult
- {
- /**
- * The status of the result.
- * @var int
- */
- private $status_code;
-
- /**
- * Message returned along with the status which is almost always an error message.
- * This must be human readable, understandable and localised.
- * @var string
- */
- private $message;
-
- /**
- * Result store.
- * Attach result specific informaton here.
- *
- * @var mixed. Should probably be an object of some sort.
- */
- private $result;
-
- /**
- * Set a status code and optional message.
- *
- * @param int $status The status code.
- * @param string $message The message.
- */
- protected function setStatusCode($status, $message = "")
- {
- $this->status_code = $status;
- $this->message = $message;
- }
-
- /**
- * Set the result.
- *
- * @param mixed $result
- */
- protected function setResult($result) { $this->result = $result; }
-
- protected function getStatusCode() { return $this->status_code; }
- protected function getStatusMessage() { return $this->message; }
- protected function getResult() { return $this->result; }
-
- /**
- * Serialise to a standard class.
- *
- * DEVNOTE: The API is only interested in data, we can not easily serialise
- * custom classes without the need for 1) the other side being PHP, 2) you need to have the class
- * definition installed, 3) its the right version!
- *
- * Therefore, I'm not bothering.
- *
- * Override this to include any more specific information, however api results should be attached to the
- * class using setResult().
- *
- * if $CONFIG->debug is set then additional information about the runtime environment and authentication will be
- * returned.
- *
- * @return stdClass Object containing the serialised result.
- */
- public function export()
- {
- global $ERRORS, $CONFIG, $_PAM_HANDLERS_MSG;
-
- $result = new stdClass;
-
- $result->status = $this->getStatusCode();
- if ($this->getStatusMessage()!="") $result->message = $this->getStatusMessage();
-
- $resultdata = $this->getResult();
- if (isset($resultdata)) $result->result = $resultdata;
-
- if ((isset($CONFIG->debug)) && ($CONFIG->debug == true))
- {
- if (count($ERRORS))
- $result->runtime_errors = $ERRORS;
-
- if (count($_PAM_HANDLERS_MSG))
- $result->pam = $_PAM_HANDLERS_MSG;
- }
+ private $result;
- return $result;
- }
- }
-
/**
- * SuccessResult
- * Generic success result class, extend if you want to do something special.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
+ * Set a status code and optional message.
+ *
+ * @param int $status The status code.
+ * @param string $message The message.
*/
- class SuccessResult extends GenericResult
- {
- public static $RESULT_SUCCESS = 0; // Do not change this from 0
-
- public function SuccessResult($result)
- {
- $this->setResult($result);
- $this->setStatusCode(SuccessResult::$RESULT_SUCCESS);
- }
-
- public static function getInstance($result)
- {
- // Return a new error object.
- return new SuccessResult($result);
- }
+ protected function setStatusCode($status, $message = "") {
+ $this->status_code = $status;
+ $this->message = $message;
}
-
+
/**
- * ErrorResult
- * The error result class.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
+ * Set the result.
+ *
+ * @param mixed $result
*/
- class ErrorResult extends GenericResult
- {
- public static $RESULT_FAIL = -1 ; // Fail with no specific code
-
- public static $RESULT_FAIL_APIKEY_DISABLED = -30;
- public static $RESULT_FAIL_APIKEY_INACTIVE = -31;
- public static $RESULT_FAIL_APIKEY_INVALID = -32;
-
- public static $RESULT_FAIL_AUTHTOKEN = -20; // Invalid, expired or missing auth token
-
- public function ErrorResult($message, $code = "", Exception $exception = NULL)
- {
- if ($code == "")
- $code = ErrorResult::$RESULT_FAIL;
-
- if ($exception!=NULL)
- $this->setResult($exception->__toString());
-
- $this->setStatusCode($code, $message);
- }
-
- /**
- * Get a new instance of the ErrorResult.
- *
- * @param string $message
- * @param int $code
- * @param Exception $exception Optional exception for generating a stack trace.
- */
- public static function getInstance($message, $code = "", Exception $exception = NULL)
- {
- // Return a new error object.
- return new ErrorResult($message, $code, $exception);
- }
+ protected function setResult($result) {
+ $this->result = $result;
+ }
+
+ protected function getStatusCode() {
+ return $this->status_code;
+ }
+
+ protected function getStatusMessage() {
+ return $this->message;
}
-
- // Caching of HMACs ///////////////////////////////////////////////////////////////////////
-
+
+ protected function getResult() {
+ return $this->result;
+ }
+
/**
- * ElggHMACCache
- * Store cached data in a temporary database, only used by the HMAC stuff.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage API
+ * Serialise to a standard class.
+ *
+ * DEVNOTE: The API is only interested in data, we can not easily serialise
+ * custom classes without the need for 1) the other side being PHP, 2) you need to have the class
+ * definition installed, 3) its the right version!
+ *
+ * Therefore, I'm not bothering.
+ *
+ * Override this to include any more specific information, however api results should be attached to the
+ * class using setResult().
+ *
+ * if $CONFIG->debug is set then additional information about the runtime environment and authentication will be
+ * returned.
+ *
+ * @return stdClass Object containing the serialised result.
*/
- class ElggHMACCache extends ElggCache
- {
- /**
- * Set the Elgg cache.
- *
- * @param int $max_age Maximum age in seconds, 0 if no limit.
- */
- function __construct($max_age = 0)
- {
- $this->set_variable("max_age", $max_age);
+ public function export() {
+ global $ERRORS, $CONFIG, $_PAM_HANDLERS_MSG;
+
+ $result = new stdClass;
+
+ $result->status = $this->getStatusCode();
+ if ($this->getStatusMessage()!="") {
+ $result->message = $this->getStatusMessage();
}
-
- /**
- * Save a key
- *
- * @param string $key
- * @param string $data
- * @return boolean
- */
- public function save($key, $data)
- {
- global $CONFIG;
-
- $key = sanitise_string($key);
- $time = time();
-
- return insert_data("INSERT into {$CONFIG->dbprefix}hmac_cache (hmac, ts) VALUES ('$key', '$time')");
+
+ $resultdata = $this->getResult();
+ if (isset($resultdata)) {
+ $result->result = $resultdata;
}
-
- /**
- * Load a key
- *
- * @param string $key
- * @param int $offset
- * @param int $limit
- * @return string
- */
- public function load($key, $offset = 0, $limit = null)
- {
- global $CONFIG;
-
- $key = sanitise_string($key);
-
- $row = get_data_row("SELECT * from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
- if ($row)
- return $row->hmac;
-
- return false;
+
+ if ((isset($CONFIG->debug)) && ($CONFIG->debug == true)) {
+ if (count($ERRORS)) {
+ $result->runtime_errors = $ERRORS;
+ }
+
+ if (count($_PAM_HANDLERS_MSG)) {
+ $result->pam = $_PAM_HANDLERS_MSG;
+ }
}
-
- /**
- * Invalidate a given key.
- *
- * @param string $key
- * @return bool
- */
- public function delete($key)
- {
- global $CONFIG;
-
- $key = sanitise_string($key);
-
- return delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
+
+ return $result;
+ }
+}
+
+/**
+ * SuccessResult
+ * Generic success result class, extend if you want to do something special.
+ *
+ * @author Curverider Ltd <info@elgg.com>
+ * @package Elgg
+ * @subpackage Core
+ */
+class SuccessResult extends GenericResult {
+ public static $RESULT_SUCCESS = 0; // Do not change this from 0
+
+ public function SuccessResult($result) {
+ $this->setResult($result);
+ $this->setStatusCode(SuccessResult::$RESULT_SUCCESS);
+ }
+
+ public static function getInstance($result) {
+ // Return a new error object.
+ return new SuccessResult($result);
+ }
+}
+
+/**
+ * ErrorResult
+ * The error result class.
+ *
+ * @author Curverider Ltd <info@elgg.com>
+ * @package Elgg
+ * @subpackage Core
+ */
+class ErrorResult extends GenericResult {
+ // Fail with no specific code
+ public static $RESULT_FAIL = -1 ;
+
+ public static $RESULT_FAIL_APIKEY_DISABLED = -30;
+ public static $RESULT_FAIL_APIKEY_INACTIVE = -31;
+ public static $RESULT_FAIL_APIKEY_INVALID = -32;
+
+ // Invalid, expired or missing auth token
+ public static $RESULT_FAIL_AUTHTOKEN = -20;
+
+ public function ErrorResult($message, $code = "", Exception $exception = NULL) {
+ if ($code == "") {
+ $code = ErrorResult::$RESULT_FAIL;
}
-
- /**
- * Clear out all the contents of the cache.
- *
- * Not currently implemented in this cache type.
- */
- public function clear() { return true; }
-
- /**
- * Clean out old stuff.
- *
- */
- public function __destruct()
- {
- global $CONFIG;
-
- $time = time();
- $age = (int)$this->get_variable("max_age");
-
- $expires = $time-$age;
-
- delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where ts<$expires");
+
+ if ($exception!=NULL) {
+ $this->setResult($exception->__toString());
}
+
+ $this->setStatusCode($code, $message);
}
- // API Call functions /////////////////////////////////////////////////////////////////////
-
- /**
- * An array holding methods.
- * The structure of this is
- * $METHODS = array (
- * "api.method" => array (
- * "function" = 'my_function_callback'
- * "call_method" = 'GET' | 'POST'
- * "parameters" = array (
- * "variable" = array ( // NB, the order is the same as defined by your function callback
- * type => 'int' | 'bool' | 'float' | 'string'
- * required => true (default) | false
- * )
- * )
- * "require_auth_token" => true (default) | false
- * "description" => "Some human readable description"
- * )
- * )
- */
- $METHODS = array();
-
/**
- * Get the request method.
+ * Get a new instance of the ErrorResult.
+ *
+ * @param string $message
+ * @param int $code
+ * @param Exception $exception Optional exception for generating a stack trace.
*/
- function get_call_method()
- {
- return $_SERVER['REQUEST_METHOD'];
+ public static function getInstance($message, $code = "", Exception $exception = NULL) {
+ // Return a new error object.
+ return new ErrorResult($message, $code, $exception);
}
-
+}
+
+// Caching of HMACs ///////////////////////////////////////////////////////////////////////
+
+/**
+ * ElggHMACCache
+ * Store cached data in a temporary database, only used by the HMAC stuff.
+ *
+ * @author Curverider Ltd <info@elgg.com>
+ * @package Elgg
+ * @subpackage API
+ */
+class ElggHMACCache extends ElggCache {
/**
- * This function analyses all expected parameters for a given method, returning them in an associated array from
- * input.
- *
- * This ensures that they are sanitised and that no superfluous commands are registered. It also means that
- * hmacs work through the page handler.
+ * Set the Elgg cache.
*
- * @param string $method The method
- * @return Array containing commands and values, including method and api
+ * @param int $max_age Maximum age in seconds, 0 if no limit.
*/
- function get_parameters_for_method($method)
- {
- global $CONFIG, $METHODS;
-
- $method = sanitise_string($method);
- $sanitised = array();
-
- foreach ($CONFIG->input as $k => $v)
- {
- if ((isset($METHODS[$method]['parameters'][$k])) || ($k == 'auth_token') || ($k == 'method'))
- $sanitised[$k] = get_input($k); // Make things go through the sanitiser
- }
-
- return $sanitised;
+ function __construct($max_age = 0) {
+ $this->set_variable("max_age", $max_age);
}
-
+
/**
- * Obtain a token for a user.
+ * Save a key
*
- * @param string $username The username
- * @param string $password The password
+ * @param string $key
+ * @param string $data
+ * @return boolean
*/
- function obtain_user_token($username, $password)
- {
+ public function save($key, $data) {
global $CONFIG;
-
- $site = $CONFIG->site_id;
- $user = get_user_by_username($username);
+
+ $key = sanitise_string($key);
$time = time();
- $time += 60*60;
- $token = md5(rand(). microtime() . $username . $password . $time . $site);
-
- 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'"))
- return $token;
-
- return false;
+
+ return insert_data("INSERT into {$CONFIG->dbprefix}hmac_cache (hmac, ts) VALUES ('$key', '$time')");
}
-
+
/**
- * Validate a token against a given site.
- *
- * 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.
- * @return mixed The user id attached to the token or false.
+ * Load a key
+ *
+ * @param string $key
+ * @param int $offset
+ * @param int $limit
+ * @return string
*/
- function validate_user_token($site, $token)
- {
+ public function load($key, $offset = 0, $limit = null) {
global $CONFIG;
-
- $site = (int)$site;
- $token = sanitise_string($token);
-
- if (!$site) throw new ConfigurationException(elgg_echo('ConfigurationException:NoSiteID'));
-
- $time = time();
-
- $user = get_data_row("SELECT * from {$CONFIG->dbprefix}users_apisessions where token='$token' and site_guid=$site and $time < expires");
- if ($user)
- return $user->user_guid;
-
+
+ $key = sanitise_string($key);
+
+ $row = get_data_row("SELECT * from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
+ if ($row) {
+ return $row->hmac;
+ }
+
return false;
}
-
+
/**
- * Expose an arbitrary function as an api call.
- *
- * Limitations: Currently can not expose functions which expect objects.
- *
- * @param string $method The api name to expose this as, eg "myapi.dosomething"
- * @param string $function Your function callback.
- * @param array $parameters Optional list of parameters in the same order as in your function, with optional parameters last.
- * This array should be in the format
- * "variable" = array (
- * type => 'int' | 'bool' | 'float' | 'string' | 'array'
- * required => true (default) | false
- * )
- * @param string $description Optional human readable description of the function.
- * @param string $call_method Define what call method should be used for this function.
- * @param bool $require_auth_token Whether this requires a user authentication token or not (default is true).
- * @param bool $anonymous Can anonymous (non-authenticated in any way) users execute this call.
+ * Invalidate a given key.
+ *
+ * @param string $key
* @return bool
*/
- function expose_function($method, $function, array $parameters = NULL, $description = "", $call_method = "GET", $require_auth_token = true, $anonymous = false)
- {
- global $METHODS;
-
- if (
- ($method!="") &&
- ($function!="")
- )
- {
- $METHODS[$method] = array();
-
- $METHODS[$method]["function"] = $function;
-
- if ($parameters!=NULL)
- $METHODS[$method]["parameters"] = $parameters;
-
- $call_method = strtoupper($call_method);
- switch ($call_method)
- {
- case 'POST' : $METHODS[$method]["call_method"] = 'POST'; break;
- case 'GET' : $METHODS[$method]["call_method"] = 'GET'; break;
- default :
- throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedMethod'), $method));
- }
-
- $METHODS[$method]["description"] = $description;
-
- $METHODS[$method]["require_auth_token"] = $require_auth_token;
-
- $METHODS[$method]["anonymous"] = $anonymous;
-
- return true;
- }
-
- return false;
+ public function delete($key) {
+ global $CONFIG;
+
+ $key = sanitise_string($key);
+
+ return delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
}
-
+
+ /**
+ * Clear out all the contents of the cache.
+ *
+ * Not currently implemented in this cache type.
+ */
+ public function clear() {
+ return true;
+ }
+
/**
- * Executes a method.
- * A method is a function which you have previously exposed using expose_function.
+ * Clean out old stuff.
*
- * @param string $method Method, e.g. "foo.bar"
- * @param array $parameters Array of parameters in the format "variable" => "value", thse will be sanitised before being fed to your handler.
- * @param string $token The authentication token to authorise this method call.
- * @return GenericResult The result of the execution.
- * @throws APIException, SecurityException
*/
- function execute_method($method, array $parameters, $token = "")
- {
- global $METHODS, $CONFIG;
-
- // Sanity check
- $method = sanitise_string($method);
- $token = sanitise_string($token);
-
- // See if we can find the method handler
- if ((isset($METHODS[$method]["function"])) && (is_callable($METHODS[$method]["function"])))
- {
- // See if this is being made with the right call method
- if (strcmp(get_call_method(), $METHODS[$method]["call_method"])==0)
- {
- $serialised_parameters = "";
-
-
- // If we have parameters then we need to sanitise the parameters.
- if ((isset($METHODS[$method]["parameters"])) && (is_array($METHODS[$method]["parameters"])))
- {
- foreach ($METHODS[$method]["parameters"] as $key => $value)
- {
- if (
- (is_array($value)) // Check that this is an array
- && (isset($value['type'])) // Check we have a type defined
- )
- {
- // Check that the variable is present in the request
-
- if (
- (!isset($parameters[$key])) || // No parameter
- ((!isset($value['required'])) || ($value['required']==true)) // Or not optional
- )
+ public function __destruct() {
+ global $CONFIG;
+
+ $time = time();
+ $age = (int)$this->get_variable("max_age");
+
+ $expires = $time-$age;
+
+ delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where ts<$expires");
+ }
+}
+
+// API Call functions /////////////////////////////////////////////////////////////////////
+
+/**
+ * An array holding methods.
+ * The structure of this is
+ * $METHODS = array (
+ * "api.method" => array (
+ * "function" = 'my_function_callback'
+ * "call_method" = 'GET' | 'POST'
+ * "parameters" = array (
+ * "variable" = array ( // NB, the order is the same as defined by your function callback
+ * type => 'int' | 'bool' | 'float' | 'string'
+ * required => true (default) | false
+ * )
+ * )
+ * "require_auth_token" => true (default) | false
+ * "description" => "Some human readable description"
+ * )
+ * )
+ */
+$METHODS = array();
+
+/**
+ * Get the request method.
+ */
+function get_call_method() {
+ return $_SERVER['REQUEST_METHOD'];
+}
+
+/**
+ * This function analyses all expected parameters for a given method, returning them in an associated array from
+ * input.
+ *
+ * This ensures that they are sanitised and that no superfluous commands are registered. It also means that
+ * hmacs work through the page handler.
+ *
+ * @param string $method The method
+ * @return Array containing commands and values, including method and api
+ */
+function get_parameters_for_method($method) {
+ global $CONFIG, $METHODS;
+
+ $method = sanitise_string($method);
+ $sanitised = array();
+
+ foreach ($CONFIG->input as $k => $v) {
+ if ((isset($METHODS[$method]['parameters'][$k])) || ($k == 'auth_token') || ($k == 'method')) {
+ // Make things go through the sanitiser
+ $sanitised[$k] = get_input($k);
+ }
+ }
+
+ return $sanitised;
+}
+
+/**
+ * Obtain a token for a user.
+ *
+ * @param string $username The username
+ * @param string $password The password
+ */
+function obtain_user_token($username, $password) {
+ global $CONFIG;
+
+ $site = $CONFIG->site_id;
+ $user = get_user_by_username($username);
+ $time = time();
+ $time += 60*60;
+ $token = md5(rand(). microtime() . $username . $password . $time . $site);
+
+ 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'")) {
+ return $token;
+ }
+
+
+ return false;
+}
+
+/**
+ * Validate a token against a given site.
+ *
+ * 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.
+ * @return mixed The user id attached to the token or false.
+ */
+function validate_user_token($site, $token) {
+ global $CONFIG;
+
+ $site = (int)$site;
+ $token = sanitise_string($token);
+
+ if (!$site) {
+ throw new ConfigurationException(elgg_echo('ConfigurationException:NoSiteID'));
+ }
+
+ $time = time();
+
+ $user = get_data_row("SELECT * from {$CONFIG->dbprefix}users_apisessions
+ where token='$token' and site_guid=$site and $time < expires");
+
+ if ($user) {
+ return $user->user_guid;
+ }
+
+ return false;
+}
+
+/**
+ * Expose an arbitrary function as an api call.
+ *
+ * Limitations: Currently can not expose functions which expect objects.
+ *
+ * @param string $method The api name to expose this as, eg "myapi.dosomething"
+ * @param string $function Your function callback.
+ * @param array $parameters Optional list of parameters in the same order as in your function, with optional parameters last.
+ * This array should be in the format
+ * "variable" = array (
+ * type => 'int' | 'bool' | 'float' | 'string' | 'array'
+ * required => true (default) | false
+ * )
+ * @param string $description Optional human readable description of the function.
+ * @param string $call_method Define what call method should be used for this function.
+ * @param bool $require_auth_token Whether this requires a user authentication token or not (default is true).
+ * @param bool $anonymous Can anonymous (non-authenticated in any way) users execute this call.
+ * @return bool
+ */
+function expose_function($method, $function, array $parameters = NULL, $description = "", $call_method = "GET", $require_auth_token = true, $anonymous = false) {
+ global $METHODS;
+
+ if (($method!="") && ($function!="")) {
+ $METHODS[$method] = array();
+
+ $METHODS[$method]["function"] = $function;
+
+ if ($parameters!=NULL) {
+ $METHODS[$method]["parameters"] = $parameters;
+ }
+
+ $call_method = strtoupper($call_method);
+ switch ($call_method) {
+ case 'POST' :
+ $METHODS[$method]["call_method"] = 'POST';
+ break;
+ case 'GET' :
+ $METHODS[$method]["call_method"] = 'GET';
+ break;
+ default :
+ throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedMethod'), $method));
+ }
+
+ $METHODS[$method]["description"] = $description;
+
+ $METHODS[$method]["require_auth_token"] = $require_auth_token;
+
+ $METHODS[$method]["anonymous"] = $anonymous;
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Executes a method.
+ * A method is a function which you have previously exposed using expose_function.
+ *
+ * @param string $method Method, e.g. "foo.bar"
+ * @param array $parameters Array of parameters in the format "variable" => "value", thse will be sanitised before being fed to your handler.
+ * @param string $token The authentication token to authorise this method call.
+ * @return GenericResult The result of the execution.
+ * @throws APIException, SecurityException
+ */
+function execute_method($method, array $parameters, $token = "") {
+ global $METHODS, $CONFIG;
+
+ // Sanity check
+ $method = sanitise_string($method);
+ $token = sanitise_string($token);
+
+ // See if we can find the method handler
+ if ((isset($METHODS[$method]["function"])) && (is_callable($METHODS[$method]["function"]))) {
+ // See if this is being made with the right call method
+ if (strcmp(get_call_method(), $METHODS[$method]["call_method"])==0) {
+ $serialised_parameters = "";
+
+ // If we have parameters then we need to sanitise the parameters.
+ if ((isset($METHODS[$method]["parameters"])) && (is_array($METHODS[$method]["parameters"]))) {
+ foreach ($METHODS[$method]["parameters"] as $key => $value) {
+
+ if ((is_array($value)) && (isset($value['type']))) {
+ // Check that the variable is present in the request
+ if ((!isset($parameters[$key]))
+ || ((!isset($value['required']))
+ || ($value['required']==true))) {
throw new APIException(sprintf(elgg_echo('APIException:MissingParameterInMethod'), $key, $method));
- else
- {
- // Avoid debug error
- if (isset($parameters[$key]))
- {
- // Set variables casting to type.
- switch (strtolower($value['type']))
- {
- case 'int':
- case 'integer' : $serialised_parameters .= "," . (int)trim($parameters[$key]); break;
- case 'bool':
- case 'boolean':
- if (strcasecmp(trim($parameters[$key]), "false")==0)
- $parameters[$key]='';
-
- $serialised_parameters .= "," . (bool)trim($parameters[$key]);
- break;
- case 'string': $serialised_parameters .= ",'" . (string)mysql_real_escape_string(trim($parameters[$key])) . "'";
- break;
- case 'float': $serialised_parameters .= "," . (float)trim($parameters[$key]);
- break;
- case 'array':
- $array = "array(";
-
- if (is_array($parameters[$key]))
- {
- foreach ($parameters[$key] as $k => $v)
- {
- $k = sanitise_string($k);
- $v = sanitise_string($v);
-
- $array .= "'$k'=>'$v',";
- }
-
- $array = trim($array,",");
- }
- else
- throw APIException(sprintf(elgg_echo('APIException:ParameterNotArray'), $key));
-
- $array .= ")";
-
- $serialised_parameters .= $array;
- break;
-
- default : throw new APIException(sprintf(elgg_echo('APIException:UnrecognisedTypeCast'), $value['type'], $key, $method));
- }
+ } else {
+ // Avoid debug error
+ if (isset($parameters[$key])) {
+ // Set variables casting to type.
+ switch (strtolower($value['type'])) {
+ case 'int':
+ case 'integer' :
+ $serialised_parameters .= "," . (int)trim($parameters[$key]); break;
+ case 'bool':
+ case 'boolean':
+ if (strcasecmp(trim($parameters[$key]), "false")==0) {
+ $parameters[$key]='';
+ }
+
+ $serialised_parameters .= "," . (bool)trim($parameters[$key]);
+ break;
+ case 'string':
+ $serialised_parameters .= ",'" . (string)mysql_real_escape_string(trim($parameters[$key])) . "'";
+ break;
+ case 'float':
+ $serialised_parameters .= "," . (float)trim($parameters[$key]);
+ break;
+ case 'array':
+ $array = "array(";
+
+ if (is_array($parameters[$key])) {
+ foreach ($parameters[$key] as $k => $v) {
+ $k = sanitise_string($k);
+ $v = sanitise_string($v);
+
+ $array .= "'$k'=>'$v',";
+ }
+
+ $array = trim($array,",");
+ } else {
+ throw APIException(sprintf(elgg_echo('APIException:ParameterNotArray'), $key));
+ }
+
+ $array .= ")";
+
+ $serialised_parameters .= $array;
+ break;
+
+ default :
+ throw new APIException(sprintf(elgg_echo('APIException:UnrecognisedTypeCast'), $value['type'], $key, $method));
}
}
}
- else
- throw new APIException(sprintf(elgg_echo('APIException:InvalidParameter'), $key, $method));
+ } else {
+ throw new APIException(sprintf(elgg_echo('APIException:InvalidParameter'), $key, $method));
}
}
-
- // Execute function: Construct function and calling parameters
- $function = $METHODS[$method]["function"];
- $serialised_parameters = trim($serialised_parameters, ", ");
-
- $result = eval("return $function($serialised_parameters);");
-
- // Sanity check result
- if ($result instanceof GenericResult) // If this function returns an api result itself, just return it
- return $result;
-
- if ($result === FALSE)
- throw new APIException(sprintf(elgg_echo('APIException:FunctionParseError'), $function, $serialised_parameters));
-
- if ($result === NULL)
- throw new APIException(sprintf(elgg_echo('APIException:FunctionNoReturn'), $function, $serialised_parameters)); // If no value
-
- return SuccessResult::getInstance($result); // Otherwise assume that the call was successful and return it as a success object.
-
- }
- else
- throw new CallException(sprintf(elgg_echo('CallException:InvalidCallMethod'), $method, $METHODS[$method]["call_method"]));
+ }
+
+ // Execute function: Construct function and calling parameters
+ $function = $METHODS[$method]["function"];
+ $serialised_parameters = trim($serialised_parameters, ", ");
+
+ $result = eval("return $function($serialised_parameters);");
+
+ // Sanity check result
+ // If this function returns an api result itself, just return it
+ if ($result instanceof GenericResult) {
+ return $result;
+ }
+
+ if ($result === FALSE) {
+ throw new APIException(sprintf(elgg_echo('APIException:FunctionParseError'), $function, $serialised_parameters));
+ }
+
+ if ($result === NULL) {
+ // If no value
+ throw new APIException(sprintf(elgg_echo('APIException:FunctionNoReturn'), $function, $serialised_parameters));
+ }
+
+ // Otherwise assume that the call was successful and return it as a success object.
+ return SuccessResult::getInstance($result);
+
+ } else {
+ throw new CallException(sprintf(elgg_echo('CallException:InvalidCallMethod'), $method, $METHODS[$method]["call_method"]));
}
-
- // Return an error if not found
- throw new APIException(sprintf(elgg_echo('APIException:MethodCallNotImplemented'), $method));
}
-
- // System functions ///////////////////////////////////////////////////////////////////////
-
- /**
- * Simple api to return a list of all api's installed on the system.
- */
- function list_all_apis()
- {
- global $METHODS;
- return $METHODS;
- }
-
- // Expose some system api functions
- expose_function("system.api.list", "list_all_apis", NULL, elgg_echo("system.api.list"), "GET", false);
- /**
- * The auth.gettoken API.
- * This API call lets a user log in, returning an authentication token which can be used
- * in leu of a username and password login from then on.
- *
- * @param string username Username
- * @param string password Clear text password
- */
- function auth_gettoken($username, $password)
- {
- if (authenticate($username, $password))
- {
- $token = obtain_user_token($username, $password);
- if ($token)
- return $token;
+ // Return an error if not found
+ throw new APIException(sprintf(elgg_echo('APIException:MethodCallNotImplemented'), $method));
+}
+
+// System functions ///////////////////////////////////////////////////////////////////////
+
+/**
+ * Simple api to return a list of all api's installed on the system.
+ */
+function list_all_apis() {
+ global $METHODS;
+ return $METHODS;
+}
+
+// Expose some system api functions
+expose_function("system.api.list", "list_all_apis", NULL, elgg_echo("system.api.list"), "GET", false);
+
+/**
+ * The auth.gettoken API.
+ * This API call lets a user log in, returning an authentication token which can be used
+ * in leu of a username and password login from then on.
+ *
+ * @param string username Username
+ * @param string password Clear text password
+ */
+function auth_gettoken($username, $password) {
+ if (authenticate($username, $password)) {
+ $token = obtain_user_token($username, $password);
+ if ($token) {
+ return $token;
}
-
- throw new SecurityException(elgg_echo('SecurityException:authenticationfailed'));
}
-
- // The authentication token api
- expose_function("auth.gettoken", "auth_gettoken", array(
- "username" => array (
- 'type' => 'string'
- ),
- "password" => array (
- 'type' => 'string'
- )
- ), elgg_echo('auth.gettoken'), "GET", false, false);
-
-
- // PAM AUTH HMAC functions ////////////////////////////////////////////////////////////////
-
- /**
- * Map various algorithms to their PHP equivs.
- * This also gives us an easy way to disable algorithms.
- *
- * @param string $algo The algorithm
- * @return string The php algorithm
- * @throws APIException if an algorithm is not supported.
- */
- function map_api_hash($algo)
- {
- $algo = strtolower(sanitise_string($algo));
- $supported_algos = array(
- "md5" => "md5", // TODO: Consider phasing this out
- "sha" => "sha1", // alias for sha1
- "sha1" => "sha1",
- "sha256" => "sha256"
- );
-
- if (array_key_exists($algo, $supported_algos))
- return $supported_algos[$algo];
-
- throw new APIException(sprintf(elgg_echo('APIException:AlgorithmNotSupported'), $algo));
+
+ throw new SecurityException(elgg_echo('SecurityException:authenticationfailed'));
+}
+
+// The authentication token api
+expose_function("auth.gettoken", "auth_gettoken", array(
+ "username" => array (
+ 'type' => 'string'
+ ),
+ "password" => array (
+ 'type' => 'string'
+ )
+), elgg_echo('auth.gettoken'), "GET", false, false);
+
+
+// PAM AUTH HMAC functions ////////////////////////////////////////////////////////////////
+
+/**
+ * Map various algorithms to their PHP equivs.
+ * This also gives us an easy way to disable algorithms.
+ *
+ * @param string $algo The algorithm
+ * @return string The php algorithm
+ * @throws APIException if an algorithm is not supported.
+ */
+function map_api_hash($algo) {
+ $algo = strtolower(sanitise_string($algo));
+ $supported_algos = array(
+ "md5" => "md5", // TODO: Consider phasing this out
+ "sha" => "sha1", // alias for sha1
+ "sha1" => "sha1",
+ "sha256" => "sha256"
+ );
+
+ if (array_key_exists($algo, $supported_algos)) {
+ return $supported_algos[$algo];
}
-
- /**
- * Calculate the HMAC for the query.
- * This function signs an api request using the information provided and is then verified by
- * searunner.
- *
- * @param $algo string The HMAC algorithm used as stored in X-Searunner-hmac-algo.
- * @param $time string String representation of unix time as stored in X-Searunner-time.
- * @param $api_key string Your api key.
- * @param $secret string Your secret key.
- * @param $get_variables string URLEncoded string representation of the get variable parameters, eg "format=php&method=searunner.test".
- * @param $post_hash string Optional sha1 hash of the post data.
- * @return string The HMAC string.
- */
- function calculate_hmac($algo, $time, $api_key, $secret_key, $get_variables, $post_hash = "")
- {
- global $CONFIG;
-
- if ((isset($CONFIG)) && ($CONFIG->debug))
- error_log("HMAC Parts: $algo, $time, $api_key, $secret_key, $get_variables, $post_hash");
-
- $ctx = hash_init(map_api_hash($algo), HASH_HMAC, $secret_key);
-
- hash_update($ctx, trim($time));
- hash_update($ctx, trim($api_key));
- hash_update($ctx, trim($get_variables));
- if (trim($post_hash)!="") hash_update($ctx, trim($post_hash));
-
- return hash_final($ctx);
+
+ throw new APIException(sprintf(elgg_echo('APIException:AlgorithmNotSupported'), $algo));
+}
+
+/**
+ * Calculate the HMAC for the query.
+ * This function signs an api request using the information provided and is then verified by
+ * searunner.
+ *
+ * @param $algo string The HMAC algorithm used as stored in X-Searunner-hmac-algo.
+ * @param $time string String representation of unix time as stored in X-Searunner-time.
+ * @param $api_key string Your api key.
+ * @param $secret string Your secret key.
+ * @param $get_variables string URLEncoded string representation of the get variable parameters, eg "format=php&method=searunner.test".
+ * @param $post_hash string Optional sha1 hash of the post data.
+ * @return string The HMAC string.
+ */
+function calculate_hmac($algo, $time, $api_key, $secret_key, $get_variables, $post_hash = "") {
+ global $CONFIG;
+
+ if ((isset($CONFIG)) && ($CONFIG->debug)) {
+ error_log("HMAC Parts: $algo, $time, $api_key, $secret_key, $get_variables, $post_hash");
}
- /**
- * Calculate a hash for some post data.
- *
- * TODO: Work out how to handle really large bits of data.
- *
- * @param $postdata string The post data.
- * @param $algo string The algorithm used.
- * @return string The hash.
- */
- function calculate_posthash($postdata, $algo)
- {
- $ctx = hash_init(map_api_hash($algo));
+ $ctx = hash_init(map_api_hash($algo), HASH_HMAC, $secret_key);
+
+ hash_update($ctx, trim($time));
+ hash_update($ctx, trim($api_key));
+ hash_update($ctx, trim($get_variables));
+ if (trim($post_hash)!="") {
+ hash_update($ctx, trim($post_hash));
+ }
+
+ return hash_final($ctx);
+}
+
+/**
+ * Calculate a hash for some post data.
+ *
+ * TODO: Work out how to handle really large bits of data.
+ *
+ * @param $postdata string The post data.
+ * @param $algo string The algorithm used.
+ * @return string The hash.
+ */
+function calculate_posthash($postdata, $algo) {
+ $ctx = hash_init(map_api_hash($algo));
+
+ hash_update($ctx, $postdata);
- hash_update($ctx, $postdata);
+ return hash_final($ctx);
+}
- return hash_final($ctx);
+/**
+ * This function will do two things. Firstly it verifys that a $hmac hasn't been seen before, and
+ * secondly it will add the given hmac to the cache.
+ *
+ * @param $hmac The hmac string.
+ * @return bool True if replay detected, false if not.
+ */
+function cache_hmac_check_replay($hmac) {
+ // cache lifetime is 25 hours (see time window in get_and_validate_api_headers() )
+ $cache = new ElggHMACCache(90000);
+
+ if (!$cache->load($hmac)) {
+ $cache->save($hmac, $hmac);
+
+ return false;
}
-
- /**
- * This function will do two things. Firstly it verifys that a $hmac hasn't been seen before, and
- * secondly it will add the given hmac to the cache.
- *
- * @param $hmac The hmac string.
- * @return bool True if replay detected, false if not.
- */
- function cache_hmac_check_replay($hmac)
- {
- $cache = new ElggHMACCache(90000); // cache lifetime is 25 hours (see time window in get_and_validate_api_headers() )
-
- if (!$cache->load($hmac))
- {
- $cache->save($hmac, $hmac);
-
- return false;
- }
-
- return true;
+
+ return true;
+}
+
+/**
+ * Find an API User's details based on the provided public api key. These users are not users in the traditional sense.
+ *
+ * @param int $site_guid The GUID of the site.
+ * @param string $api_key The API Key
+ * @return mixed stdClass representing the database row or false.
+ */
+function get_api_user($site_guid, $api_key) {
+ global $CONFIG;
+
+ $api_key = sanitise_string($api_key);
+ $site_guid = (int)$site_guid;
+
+ return get_data_row("SELECT * from {$CONFIG->dbprefix}api_users where api_key='$api_key' and site_guid=$site_guid and active=1");
+}
+
+/**
+ * Revoke an api user key.
+ *
+ * @param int $site_guid The GUID of the site.
+ * @param string $api_key The API Key (public).
+ */
+function remove_api_user($site_guid, $api_key) {
+ global $CONFIG;
+
+ $keypair = get_api_user($site_guid, $api_key);
+ if ($keypair) {
+ return delete_data("DELETE from {$CONFIG->dbprefix}api_users where id={$keypair->id}");
}
-
- /**
- * Find an API User's details based on the provided public api key. These users are not users in the traditional sense.
- *
- * @param int $site_guid The GUID of the site.
- * @param string $api_key The API Key
- * @return mixed stdClass representing the database row or false.
- */
- function get_api_user($site_guid, $api_key)
- {
- global $CONFIG;
-
- $api_key = sanitise_string($api_key);
- $site_guid = (int)$site_guid;
-
- return get_data_row("SELECT * from {$CONFIG->dbprefix}api_users where api_key='$api_key' and site_guid=$site_guid and active=1");
+
+ 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);
}
-
- /**
- * Revoke an api user key.
- *
- * @param int $site_guid The GUID of the site.
- * @param string $api_key The API Key (public).
- */
- function remove_api_user($site_guid, $api_key)
- {
- global $CONFIG;
-
- $keypair = get_api_user($site_guid, $api_key);
- if ($keypair)
- return delete_data("DELETE from {$CONFIG->dbprefix}api_users where id={$keypair->id}");
-
- return false;
+
+ return false;
+}
+
+/**
+ * This function looks at the super-global variable $_SERVER and extracts the various
+ * header variables needed to pass to the validation functions after performing basic validation.
+ *
+ * @return stdClass Containing all the values.
+ * @throws APIException Detailing any error.
+ */
+function get_and_validate_api_headers() {
+ $result = new stdClass;
+
+ $result->method = get_call_method();
+ // Only allow these methods
+ if (($result->method != "GET") && ($result->method!= "POST")) {
+ throw new APIException(elgg_echo('APIException:NotGetOrPost'));
}
-
- /**
- * 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);
-
- if (insert_data("INSERT into {$CONFIG->dbprefix}api_users (site_guid, api_key, secret) values ($site_guid, '$public', '$secret')"))
- return get_api_user($site_guid, $public);
-
- return false;
+
+ $result->api_key = $_SERVER['HTTP_X_ELGG_APIKEY'];
+ if ($result->api_key == "") {
+ throw new APIException(elgg_echo('APIException:MissingAPIKey'));
}
-
- /**
- * This function looks at the super-global variable $_SERVER and extracts the various
- * header variables needed to pass to the validation functions after performing basic validation.
- *
- * @return stdClass Containing all the values.
- * @throws APIException Detailing any error.
- */
- function get_and_validate_api_headers()
- {
- $result = new stdClass;
-
- $result->method = get_call_method();
- if (($result->method != "GET") && ($result->method!= "POST")) // Only allow these methods
- throw new APIException(elgg_echo('APIException:NotGetOrPost'));
-
- $result->api_key = $_SERVER['HTTP_X_ELGG_APIKEY'];
- if ($result->api_key == "")
- throw new APIException(elgg_echo('APIException:MissingAPIKey'));
-
- $result->hmac = $_SERVER['HTTP_X_ELGG_HMAC'];
- if ($result->hmac == "")
- throw new APIException(elgg_echo('APIException:MissingHmac'));
-
- $result->hmac_algo = $_SERVER['HTTP_X_ELGG_HMAC_ALGO'];
- if ($result->hmac_algo == "")
- throw new APIException(elgg_echo('APIException:MissingHmacAlgo'));
-
- $result->time = $_SERVER['HTTP_X_ELGG_TIME'];
- if ($result->time == "")
- throw new APIException(elgg_echo('APIException:MissingTime'));
- if (($result->time<(microtime(true)-86400.00)) || ($result->time>(microtime(true)+86400.00))) // Basic timecheck, think about making this smaller if we get loads of users and the cache gets really big.
- throw new APIException(elgg_echo('APIException:TemporalDrift'));
-
- $result->get_variables = get_parameters_for_method(get_input('method')); //$_SERVER['QUERY_STRING'];
- if ($result->get_variables == "")
- throw new APIException(elgg_echo('APIException:NoQueryString'));
-
- if ($result->method=="POST")
- {
- $result->posthash = $_SERVER['HTTP_X_ELGG_POSTHASH'];
- if ($result->posthash == "")
- throw new APIException(elgg_echo('APIException:MissingPOSTHash'));
-
- $result->posthash_algo = $_SERVER['HTTP_X_ELGG_POSTHASH_ALGO'];
- if ($result->posthash_algo == "")
- throw new APIException(elgg_echo('APIException:MissingPOSTAlgo'));
-
- $result->content_type = $_SERVER['CONTENT_TYPE'];
- if ($result->content_type == "")
- throw new APIException(elgg_echo('APIException:MissingContentType'));
- }
-
- return $result;
+
+ $result->hmac = $_SERVER['HTTP_X_ELGG_HMAC'];
+ if ($result->hmac == "") {
+ throw new APIException(elgg_echo('APIException:MissingHmac'));
}
-
- /**
- * Return a sanitised form of the POST data sent to the script
- *
- * @return string
- */
- function get_post_data()
- {
- global $GLOBALS;
-
- $postdata = $GLOBALS['HTTP_RAW_POST_DATA'];
-
- // Attempt another method to return post data (incase always_populate_raw_post_data is switched off)
- if (!$postdata)
- {
- $postdata = file_get_contents('php://input');
+
+ $result->hmac_algo = $_SERVER['HTTP_X_ELGG_HMAC_ALGO'];
+ if ($result->hmac_algo == "") {
+ throw new APIException(elgg_echo('APIException:MissingHmacAlgo'));
+ }
+
+ $result->time = $_SERVER['HTTP_X_ELGG_TIME'];
+ if ($result->time == "") {
+ throw new APIException(elgg_echo('APIException:MissingTime'));
+ }
+
+ // Basic timecheck, think about making this smaller if we get loads of users and the cache gets really big.
+ if (($result->time<(microtime(true)-86400.00)) || ($result->time>(microtime(true)+86400.00))) {
+ throw new APIException(elgg_echo('APIException:TemporalDrift'));
+ }
+
+ //$_SERVER['QUERY_STRING'];
+ $result->get_variables = get_parameters_for_method(get_input('method'));
+ if ($result->get_variables == "") {
+ throw new APIException(elgg_echo('APIException:NoQueryString'));
+ }
+
+ if ($result->method=="POST") {
+ $result->posthash = $_SERVER['HTTP_X_ELGG_POSTHASH'];
+ if ($result->posthash == "") {
+ throw new APIException(elgg_echo('APIException:MissingPOSTHash'));
+ }
+
+ $result->posthash_algo = $_SERVER['HTTP_X_ELGG_POSTHASH_ALGO'];
+ if ($result->posthash_algo == "") {
+ throw new APIException(elgg_echo('APIException:MissingPOSTAlgo'));
+ }
+
+ $result->content_type = $_SERVER['CONTENT_TYPE'];
+ if ($result->content_type == "") {
+ throw new APIException(elgg_echo('APIException:MissingContentType'));
}
-
- return $postdata;
}
-
- // PAM functions //////////////////////////////////////////////////////////////////////////
- /**
- * Function that examines whether an authentication token is present returning true if it is, OR the requested
- * method doesn't require one.
- *
- * If a token is present and a validated user id is returned, that user is logged in to the current session.
- *
- * @param unknown_type $credentials
- */
- function pam_auth_usertoken($credentials = NULL)
- {
- global $METHODS, $CONFIG;
-
- $method = get_input('method');
- $token = get_input('auth_token');
-
- $validated_userid = validate_user_token($CONFIG->site_id, $token);
-
- if ($validated_userid) {
- $u = get_entity($validated_userid);
- if (!$u) return false; // Could we get the user?
- if ( (!$u instanceof ElggUser)) return false; // Not an elgg user
- if ($u->isBanned()) return false; // User is banned
- if (!login($u)) return false; // Fail if we couldn't log the user in
-
+ return $result;
+}
+
+/**
+ * Return a sanitised form of the POST data sent to the script
+ *
+ * @return string
+ */
+function get_post_data() {
+ global $GLOBALS;
+
+ $postdata = $GLOBALS['HTTP_RAW_POST_DATA'];
+
+ // Attempt another method to return post data (incase always_populate_raw_post_data is switched off)
+ if (!$postdata) {
+ $postdata = file_get_contents('php://input');
+ }
+
+ return $postdata;
+}
+
+// PAM functions //////////////////////////////////////////////////////////////////////////
+
+/**
+ * Function that examines whether an authentication token is present returning true if it is, OR the requested
+ * method doesn't require one.
+ *
+ * If a token is present and a validated user id is returned, that user is logged in to the current session.
+ *
+ * @param unknown_type $credentials
+ */
+function pam_auth_usertoken($credentials = NULL) {
+ global $METHODS, $CONFIG;
+
+ $method = get_input('method');
+ $token = get_input('auth_token');
+
+ $validated_userid = validate_user_token($CONFIG->site_id, $token);
+
+ if ($validated_userid) {
+ $u = get_entity($validated_userid);
+
+ // Could we get the user?
+ if (!$u) {
+ return false;
+ }
+
+ // Not an elgg user
+ if ( (!$u instanceof ElggUser)) {
+ return false;
+ }
+
+ // User is banned
+ if ($u->isBanned()) {
+ return false;
+ }
+
+ // Fail if we couldn't log the user in
+ if (!login($u)) {
+ return false;
}
-
- if ((!$METHODS[$method]["require_auth_token"]) || ($validated_userid) || (isloggedin())) {
- return true;
- } else
- throw new SecurityException(elgg_echo('SecurityException:AuthTokenExpired'), ErrorResult::$RESULT_FAIL_AUTHTOKEN);
-
- return false;
}
-
- /**
- * Test to see whether a given function has been declared as anonymous access (it doesn't require any auth token)
- *
- * @param unknown_type $credentials
- */
- function pam_auth_anonymous_method($credentials = NULL)
- {
- global $METHODS, $CONFIG;
-
- $method = get_input('method');
-
- if ((isset($METHODS[$method]["anonymous"])) && ($METHODS[$method]["anonymous"]))
- return true;
-
- return false;
+
+ if ((!$METHODS[$method]["require_auth_token"]) || ($validated_userid) || (isloggedin())) {
+ return true;
+ } else {
+ throw new SecurityException(elgg_echo('SecurityException:AuthTokenExpired'), ErrorResult::$RESULT_FAIL_AUTHTOKEN);
}
-
- /**
- * See if the user has a valid login sesson.
- */
- function pam_auth_session($credentials = NULL)
- {
- return isloggedin();
+
+ return false;
+}
+
+/**
+ * Test to see whether a given function has been declared as anonymous access (it doesn't require any auth token)
+ *
+ * @param unknown_type $credentials
+ */
+function pam_auth_anonymous_method($credentials = NULL) {
+ global $METHODS, $CONFIG;
+
+ $method = get_input('method');
+
+ if ((isset($METHODS[$method]["anonymous"])) && ($METHODS[$method]["anonymous"])) {
+ return true;
}
-
- /**
- * Secure authentication through headers and HMAC.
- */
- function pam_auth_hmac($credentials = NULL)
- {
- global $CONFIG;
-
- $api_header = get_and_validate_api_headers(); // Get api header
- $api_user = get_api_user($CONFIG->site_id, $api_header->api_key); // Pull API user details
-
- if ($api_user)
- {
- // Get the secret key
- $secret_key = $api_user->secret;
-
- // Serialise parameters
- $encoded_params = array();
- foreach ($api_header->get_variables as $k => $v)
- $encoded_params[] = urlencode($k).'='.urlencode($v);
- $params = implode('&', $encoded_params);
-
- // Validate HMAC
- $hmac = calculate_hmac($api_header->hmac_algo,
- $api_header->time,
- $api_header->api_key,
- $secret_key,
- $params,
- $api_header->method == 'POST' ? $api_header->posthash : "");
-
- if ((strcmp(
- $api_header->hmac,
- $hmac
- )==0) && ($api_header->hmac) && ($hmac))
- {
- // Now make sure this is not a replay
- if (!cache_hmac_check_replay($hmac))
- {
-
- // Validate post data
- if ($api_header->method=="POST")
- {
- $postdata = get_post_data();
- $calculated_posthash = calculate_posthash($postdata, $api_header->posthash_algo);
-
- if (strcmp($api_header->posthash, $calculated_posthash)!=0)
- throw new SecurityException(sprintf(elgg_echo('SecurityException:InvalidPostHash'), $calculated_posthash, $api_header->posthash));
+
+ return false;
+}
+
+/**
+ * See if the user has a valid login sesson.
+ */
+function pam_auth_session($credentials = NULL) {
+ return isloggedin();
+}
+
+/**
+ * Secure authentication through headers and HMAC.
+ */
+function pam_auth_hmac($credentials = NULL) {
+ global $CONFIG;
+
+ // Get api header
+ $api_header = get_and_validate_api_headers();
+
+ // Pull API user details
+ $api_user = get_api_user($CONFIG->site_id, $api_header->api_key);
+
+ if ($api_user) {
+ // Get the secret key
+ $secret_key = $api_user->secret;
+
+ // Serialise parameters
+ $encoded_params = array();
+ foreach ($api_header->get_variables as $k => $v) {
+ $encoded_params[] = urlencode($k).'='.urlencode($v);
+ }
+ $params = implode('&', $encoded_params);
+
+ // Validate HMAC
+ $hmac = calculate_hmac($api_header->hmac_algo,
+ $api_header->time,
+ $api_header->api_key,
+ $secret_key,
+ $params,
+ $api_header->method == 'POST' ? $api_header->posthash : "");
+
+ if ((strcmp($api_header->hmac, $hmac) == 0) && ($api_header->hmac) && ($hmac)) {
+ // Now make sure this is not a replay
+ if (!cache_hmac_check_replay($hmac)) {
+
+ // Validate post data
+ if ($api_header->method=="POST") {
+ $postdata = get_post_data();
+ $calculated_posthash = calculate_posthash($postdata, $api_header->posthash_algo);
+
+ if (strcmp($api_header->posthash, $calculated_posthash)!=0) {
+ throw new SecurityException(sprintf(elgg_echo('SecurityException:InvalidPostHash'), $calculated_posthash, $api_header->posthash));
}
-
- // If we've passed all the checks so far then we can be reasonably certain that the request is authentic, so return this fact to the PAM engine.
- return true;
}
- else
- throw new SecurityException(elgg_echo('SecurityException:DupePacket'));
+
+ // If we've passed all the checks so far then we can be reasonably certain that the request is authentic, so return this fact to the PAM engine.
+ return true;
+ } else {
+ throw new SecurityException(elgg_echo('SecurityException:DupePacket'));
}
- else
- throw new SecurityException("HMAC is invalid. {$api_header->hmac} != [calc]$hmac = {$api_header->hmac_algo}(**SECRET KEY**, time:{$api_header->time}, apikey:{$api_header->api_key}, get_vars:{$params}" . ($api_header->method=="POST"? "posthash:$api_header->posthash}" : ")"));
+ } else {
+ throw new SecurityException("HMAC is invalid. {$api_header->hmac} != [calc]$hmac = {$api_header->hmac_algo}(**SECRET KEY**, time:{$api_header->time}, apikey:{$api_header->api_key}, get_vars:{$params}" . ($api_header->method=="POST"? "posthash:$api_header->posthash}" : ")"));
}
- else
- throw new SecurityException(elgg_echo('SecurityException:InvalidAPIKey'),ErrorResult::$RESULT_FAIL_APIKEY_INVALID);
-
- return false;
+ } else {
+ throw new SecurityException(elgg_echo('SecurityException:InvalidAPIKey'),ErrorResult::$RESULT_FAIL_APIKEY_INVALID);
}
-
- /**
- * A bit of a hack. Basically, this combines session and hmac, so that one of them must evaluate to true in order
- * to proceed.
- *
- * This ensures that this and auth_token are evaluated separately.
- *
- * @param unknown_type $credentials
- */
- function pam_auth_session_or_hmac($credentials = NULL)
- {
- if (pam_auth_session($credentials))
- return true;
-
- if (pam_auth_hmac($credentials))
- return true;
-
- return false;
+
+ return false;
+}
+
+/**
+ * A bit of a hack. Basically, this combines session and hmac, so that one of them must evaluate to true in order
+ * to proceed.
+ *
+ * This ensures that this and auth_token are evaluated separately.
+ *
+ * @param unknown_type $credentials
+ */
+function pam_auth_session_or_hmac($credentials = NULL) {
+ if (pam_auth_session($credentials)) {
+ return true;
}
-
- // Client api functions ///////////////////////////////////////////////////////////////////
-
- $APICLIENT_LAST_CALL = NULL;
- $APICLIENT_LAST_CALL_RAW = "";
- $APICLIENT_LAST_ERROR = NULL;
-
- /**
- * Utility function to serialise a header array into its text representation.
- *
- * @param $headers array The array of headers "key" => "value"
- * @return string
- */
- function serialise_api_headers(array $headers)
- {
- $headers_str = "";
- foreach ($headers as $k => $v)
- $headers_str .= trim($k) . ": " . trim($v) . "\r\n";
+ if (pam_auth_hmac($credentials)) {
+ return true;
+ }
+
+ return false;
+}
+
+// Client api functions ///////////////////////////////////////////////////////////////////
- return trim($headers_str);
+$APICLIENT_LAST_CALL = NULL;
+$APICLIENT_LAST_CALL_RAW = "";
+$APICLIENT_LAST_ERROR = NULL;
+
+/**
+ * Utility function to serialise a header array into its text representation.
+ *
+ * @param $headers array The array of headers "key" => "value"
+ * @return string
+ */
+function serialise_api_headers(array $headers) {
+ $headers_str = "";
+
+ foreach ($headers as $k => $v) {
+ $headers_str .= trim($k) . ": " . trim($v) . "\r\n";
}
-
- /**
- * Send a raw API call to an elgg api endpoint.
- *
- * @param array $keys The api keys.
- * @param string $url URL of the endpoint.
- * @param array $call Associated array of "variable" => "value"
- * @param string $method GET or POST
- * @param string $post_data The post data
- * @param string $content_type The content type
- * @return stdClass The unserialised response object
- */
- function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_data = '', $content_type = 'application/octet-stream')
- {
- global $APICLIENT_LAST_CALL, $APICLIENT_LAST_CALL_RAW, $APICLIENT_LAST_ERROR, $CONFIG;
-
- $headers = array();
- $encoded_params = array();
-
- $method = strtoupper($method);
- switch (strtoupper($method))
- {
- case 'GET' :
- case 'POST' : break;
- default: throw new NotImplementedException(sprintf(elgg_echo('NotImplementedException:CallMethodNotImplemented'), $method));
- }
-
- // Time
- $time = microtime(true);
-
- // URL encode all the parameters, ensuring auth_token (if present) is at the end!
- foreach ($call as $k => $v){
- if ($k!='auth_token')
- $encoded_params[] = urlencode($k).'='.urlencode($v);
- }
- if ($call['auth_token'])
- $encoded_params[] = urlencode('auth_token').'='.urlencode($call['auth_token']);
- $params = implode('&', $encoded_params);
-
- // Put together the query string
- $url = $url . "?" . $params;
-
- // Construct headers
- $posthash = "";
- if ($method == 'POST') $posthash = calculate_posthash($post_data, 'md5');
-
- if ((isset($keys['public'])) && (isset($keys['private'])))
- {
- $headers['X-Elgg-apikey'] = $keys['public'];
- $headers['X-Elgg-time'] = $time;
- $headers['X-Elgg-hmac-algo'] = 'sha1';
- $headers['X-Elgg-hmac'] = calculate_hmac('sha1',
- $time,
- $keys['public'],
- $keys['private'],
- $params,
- $posthash
- );
- }
- if ($method == 'POST')
- {
- $headers['X-Elgg-posthash'] = $posthash;
- $headers['X-Elgg-posthash-algo'] = 'md5';
-
- $headers['Content-type'] = $content_type;
- $headers['Content-Length'] = strlen($post_data);
- }
-
- // Opt array
- $http_opts = array(
- 'method' => $method,
- 'header' => serialise_api_headers($headers)
- );
- if ($method == 'POST') $http_opts['content'] = $post_data;
-
- $opts = array('http' => $http_opts);
-
- // Send context
- $context = stream_context_create($opts);
-
- // Send the query and get the result and decode.
- if ((isset($CONFIG->debug)) && ($CONFIG->debug))
- error_log("APICALL: $url");
- $APICLIENT_LAST_CALL_RAW = file_get_contents($url, false, $context);
-
- $APICLIENT_LAST_CALL = unserialize($APICLIENT_LAST_CALL_RAW);
-
- if (($APICLIENT_LAST_CALL) && ($APICLIENT_LAST_CALL->status!=0))
- $APICLIENT_LAST_ERROR = $APICLIENT_LAST_CALL;
-
- return $APICLIENT_LAST_CALL;
+ return trim($headers_str);
+}
+
+/**
+ * Send a raw API call to an elgg api endpoint.
+ *
+ * @param array $keys The api keys.
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param string $method GET or POST
+ * @param string $post_data The post data
+ * @param string $content_type The content type
+ * @return stdClass The unserialised response object
+ */
+function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_data = '', $content_type = 'application/octet-stream') {
+ global $APICLIENT_LAST_CALL, $APICLIENT_LAST_CALL_RAW, $APICLIENT_LAST_ERROR, $CONFIG;
+
+ $headers = array();
+ $encoded_params = array();
+
+ $method = strtoupper($method);
+ switch (strtoupper($method)) {
+ case 'GET' :
+ case 'POST' :
+ break;
+ default:
+ $msg = sprintf(elgg_echo('NotImplementedException:CallMethodNotImplemented'), $method);
+ throw new NotImplementedException($msg);
}
- /**
- * Send a GET call
- *
- * @param string $url URL of the endpoint.
- * @param array $call Associated array of "variable" => "value"
- * @param array $keys The keys dependant on chosen authentication method
- * @return stdClass The unserialised response object
- */
- function send_api_get_call($url, array $call, array $keys) { return send_api_call($keys, $url, $call); }
-
- /**
- * Send a GET call
- *
- * @param string $url URL of the endpoint.
- * @param array $call Associated array of "variable" => "value"
- * @param array $keys The keys dependant on chosen authentication method
- * @param string $post_data The post data
- * @param string $content_type The content type
- * @return stdClass The unserialised response object
- */
- function send_api_post_call($url, array $call, array $keys, $post_data, $content_type = 'application/octet-stream') { return send_api_call($keys, $url, $call, 'POST', $post_data, $content_type); }
-
- /**
- * Return a key array suitable for the API client using the standard authentication method based on api-keys and secret keys.
- *
- * @param string $secret_key Your secret key
- * @param string $api_key Your api key
- */
- function get_standard_api_key_array($secret_key, $api_key) { return array('public' => $api_key, 'private' => $api_key); }
-
- // Error handler functions ////////////////////////////////////////////////////////////////
-
- /** Define a global array of errors */
- $ERRORS = array();
-
- /**
- * PHP Error handler function.
- * This function acts as a wrapper to catch and report PHP error messages.
- *
- * @see http://uk3.php.net/set-error-handler
- * @param unknown_type $errno
- * @param unknown_type $errmsg
- * @param unknown_type $filename
- * @param unknown_type $linenum
- * @param unknown_type $vars
- */
- function __php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars)
- {
- global $ERRORS;
-
- $error = date("Y-m-d H:i:s (T)") . ": \"" . $errmsg . "\" in file " . $filename . " (line " . $linenum . ")";
-
- switch ($errno) {
- case E_USER_ERROR:
- error_log("ERROR: " . $error);
- $ERRORS[] = "ERROR: " .$error;
-
- // Since this is a fatal error, we want to stop any further execution but do so gracefully.
- throw new Exception("ERROR: " . $error);
- break;
-
- case E_WARNING :
- case E_USER_WARNING :
- error_log("WARNING: " . $error);
- $ERRORS[] = "WARNING: " .$error;
- break;
-
- default:
- error_log("DEBUG: " . $error);
- $ERRORS[] = "DEBUG: " .$error;
+ // Time
+ $time = microtime(true);
+
+ // URL encode all the parameters, ensuring auth_token (if present) is at the end!
+ foreach ($call as $k => $v){
+ if ($k!='auth_token') {
+ $encoded_params[] = urlencode($k).'='.urlencode($v);
}
}
-
- /**
- * PHP Exception handler.
- * This is a generic exception handler for PHP exceptions. This will catch any
- * uncaught exception and return it as an ErrorResult in the requested format.
- *
- * @param Exception $exception
- */
- function __php_api_exception_handler($exception) {
-
- error_log("*** FATAL EXCEPTION (API) *** : " . $exception);
-
- page_draw($exception->getMessage(), elgg_view("api/output",
- array('result' => ErrorResult::getInstance(
- $exception->getMessage(),
- $exception->getCode() == 0 ? ErrorResult::$RESULT_FAIL : $exception->getCode(),
- $exception)
- ))
+
+ if ($call['auth_token']) {
+ $encoded_params[] = urlencode('auth_token').'='.urlencode($call['auth_token']);
+ }
+
+ $params = implode('&', $encoded_params);
+
+ // Put together the query string
+ $url = $url . "?" . $params;
+
+ // Construct headers
+ $posthash = "";
+ if ($method == 'POST') {
+ $posthash = calculate_posthash($post_data, 'md5');
+ }
+
+ if ((isset($keys['public'])) && (isset($keys['private']))) {
+ $headers['X-Elgg-apikey'] = $keys['public'];
+ $headers['X-Elgg-time'] = $time;
+ $headers['X-Elgg-hmac-algo'] = 'sha1';
+ $headers['X-Elgg-hmac'] = calculate_hmac('sha1',
+ $time,
+ $keys['public'],
+ $keys['private'],
+ $params,
+ $posthash
);
}
-
- // Initialisation & pagehandler ///////////////////////////////////////////////////////////
-
- /**
- * Initialise the API subsystem.
- *
- */
- function api_init()
- {
- // Register a page handler, so we can have nice URLs
- register_page_handler('api','api_endpoint_handler');
+ if ($method == 'POST') {
+ $headers['X-Elgg-posthash'] = $posthash;
+ $headers['X-Elgg-posthash-algo'] = 'md5';
+
+ $headers['Content-type'] = $content_type;
+ $headers['Content-Length'] = strlen($post_data);
}
-
- /**
- * Register a page handler for the various API endpoints.
- *
- * @param array $page
- */
- function api_endpoint_handler($page)
- {
- global $CONFIG;
-
- // Which view
- if ($page[1])
- {
- elgg_set_viewtype($page[1]);
-
+
+ // Opt array
+ $http_opts = array(
+ 'method' => $method,
+ 'header' => serialise_api_headers($headers)
+ );
+ if ($method == 'POST') {
+ $http_opts['content'] = $post_data;
+ }
+
+ $opts = array('http' => $http_opts);
+
+ // Send context
+ $context = stream_context_create($opts);
+
+ // Send the query and get the result and decode.
+ if ((isset($CONFIG->debug)) && ($CONFIG->debug)) {
+ error_log("APICALL: $url");
+ }
+ $APICLIENT_LAST_CALL_RAW = file_get_contents($url, false, $context);
+
+ $APICLIENT_LAST_CALL = unserialize($APICLIENT_LAST_CALL_RAW);
+
+ if (($APICLIENT_LAST_CALL) && ($APICLIENT_LAST_CALL->status!=0)) {
+ $APICLIENT_LAST_ERROR = $APICLIENT_LAST_CALL;
+ }
+
+ return $APICLIENT_LAST_CALL;
+}
+
+/**
+ * Send a GET call
+ *
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param array $keys The keys dependant on chosen authentication method
+ * @return stdClass The unserialised response object
+ */
+function send_api_get_call($url, array $call, array $keys) {
+ return send_api_call($keys, $url, $call);
+}
+
+/**
+ * Send a GET call
+ *
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param array $keys The keys dependant on chosen authentication method
+ * @param string $post_data The post data
+ * @param string $content_type The content type
+ * @return stdClass The unserialised response object
+ */
+function send_api_post_call($url, array $call, array $keys, $post_data, $content_type = 'application/octet-stream') {
+ return send_api_call($keys, $url, $call, 'POST', $post_data, $content_type);
+}
+
+/**
+ * Return a key array suitable for the API client using the standard authentication method based on api-keys and secret keys.
+ *
+ * @param string $secret_key Your secret key
+ * @param string $api_key Your api key
+ */
+function get_standard_api_key_array($secret_key, $api_key) {
+ return array('public' => $api_key, 'private' => $api_key);
+}
+
+// Error handler functions ////////////////////////////////////////////////////////////////
+
+/** Define a global array of errors */
+$ERRORS = array();
+
+/**
+ * PHP Error handler function.
+ * This function acts as a wrapper to catch and report PHP error messages.
+ *
+ * @see http://uk3.php.net/set-error-handler
+ * @param unknown_type $errno
+ * @param unknown_type $errmsg
+ * @param unknown_type $filename
+ * @param unknown_type $linenum
+ * @param unknown_type $vars
+ */
+function __php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
+ global $ERRORS;
+
+ $error = date("Y-m-d H:i:s (T)") . ": \"" . $errmsg . "\" in file " . $filename . " (line " . $linenum . ")";
+
+ switch ($errno) {
+ case E_USER_ERROR:
+ error_log("ERROR: " . $error);
+ $ERRORS[] = "ERROR: " .$error;
+
+ // Since this is a fatal error, we want to stop any further execution but do so gracefully.
+ throw new Exception("ERROR: " . $error);
+ break;
+
+ case E_WARNING :
+ case E_USER_WARNING :
+ error_log("WARNING: " . $error);
+ $ERRORS[] = "WARNING: " .$error;
+ break;
+
+ default:
+ error_log("DEBUG: " . $error);
+ $ERRORS[] = "DEBUG: " .$error;
+ }
+}
+
+/**
+ * PHP Exception handler.
+ * This is a generic exception handler for PHP exceptions. This will catch any
+ * uncaught exception and return it as an ErrorResult in the requested format.
+ *
+ * @param Exception $exception
+ */
+function __php_api_exception_handler($exception) {
+
+ error_log("*** FATAL EXCEPTION (API) *** : " . $exception);
+
+ page_draw($exception->getMessage(), elgg_view("api/output",
+ array('result' => ErrorResult::getInstance(
+ $exception->getMessage(),
+ $exception->getCode() == 0 ? ErrorResult::$RESULT_FAIL : $exception->getCode(),
+ $exception)
+ ))
+ );
+}
+
+// Initialisation & pagehandler ///////////////////////////////////////////////////////////
+
+/**
+ * Initialise the API subsystem.
+ *
+ */
+function api_init() {
+ // Register a page handler, so we can have nice URLs
+ register_page_handler('api','api_endpoint_handler');
+}
+
+/**
+ * Register a page handler for the various API endpoints.
+ *
+ * @param array $page
+ */
+function api_endpoint_handler($page) {
+ global $CONFIG;
+
+ // Which view
+ if ($page[1]) {
+ elgg_set_viewtype($page[1]);
+ }
+
+ // Which endpoint
+ if ($page[0]) {
+ switch ($page[0]) {
+ case 'rest' :
+ default : include($CONFIG->path . "services/api/rest.php");
}
-
- // Which endpoint
- if ($page[0])
- {
- switch ($page[0])
- {
- case 'rest' :
- default : include($CONFIG->path . "services/api/rest.php");
- }
- }
}
-
-
- register_elgg_event_handler('init','system','api_init');
-
-?> \ No newline at end of file
+}
+
+register_elgg_event_handler('init','system','api_init');