/** * 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 string */ function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_data = '', $content_type = 'application/octet-stream') { global $CONFIG; $headers = array(); $encoded_params = array(); $method = strtoupper($method); switch (strtoupper($method)) { case 'GET': case 'POST': break; default: $msg = elgg_echo('NotImplementedException:CallMethodNotImplemented', array($method)); throw new NotImplementedException($msg); } // Time $time = time(); // Nonce $nonce = uniqid(''); // URL encode all the parameters foreach ($call as $k => $v) { $encoded_params[] = urlencode($k) . '=' . urlencode($v); } $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-nonce'] = $nonce; $headers['X-Elgg-hmac-algo'] = 'sha1'; $headers['X-Elgg-hmac'] = calculate_hmac('sha1', $time, $nonce, $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. elgg_log("APICALL: {$url}"); $results = file_get_contents($url, false, $context); return $results; }
/** * PAM: Confirm the HMAC signature * * @return true if success - otherwise throws exception * * @throws SecurityException * @since 1.7.0 * @access private */ function api_auth_hmac() { // Get api header $api_header = get_and_validate_api_headers(); // Pull API user details $api_user = get_api_user(elgg_get_site_entity()->guid, $api_header->api_key); if (!$api_user) { throw new SecurityException(elgg_echo('SecurityException:InvalidAPIKey'), ErrorResult::$RESULT_FAIL_APIKEY_INVALID); } // Get the secret key $secret_key = $api_user->secret; // get the query string $query = _elgg_services()->request->server->get('REQUEST_URI'); $query = substr($query, strpos($query, '?') + 1); // calculate expected HMAC $hmac = calculate_hmac($api_header->hmac_algo, $api_header->time, $api_header->nonce, $api_header->api_key, $secret_key, $query, $api_header->method == 'POST' ? $api_header->posthash : ""); if ($api_header->hmac !== $hmac) { throw new SecurityException("HMAC is invalid. {$api_header->hmac} != [calc]{$hmac}"); } // Now make sure this is not a replay if (cache_hmac_check_replay($hmac)) { throw new SecurityException(elgg_echo('SecurityException:DupePacket')); } // 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) { $msg = elgg_echo('SecurityException:InvalidPostHash', array($calculated_posthash, $api_header->posthash)); throw new SecurityException($msg); } } return true; }
/** * 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; }