/** * Call the remote API server * * @param \V1\APICall $apicall_obj The APICall object we're using to make calls * @return array The array of data ready for display (Response array or error array) */ public function make_the_call(\V1\APICall $apicall_obj) { /** * RUNCLE RICK'S RAD RUN CALL :) */ $api = \V1\Model\APIs::get_api(); $account = \V1\Model\Account::get_account(); /* * When we make a call from the local server, we'll get the localhost IP. If that's the case, * we'll set our public IP. DO NOT use X-Forwarded-For in the request headers to us. It's unreliable. * We'll still set our X-Forwarded-For in case the API provider wishes to use it. */ $forwarded_for = \Input::real_ip('0.0.0.0', true); if ($internal_call = \Utility::is_internal_call()) { $forwarded_for = \Config::get('engine.call_test_ip'); } /* * Add our own headers to allow for authenticating our server and customers. We overwrite any * of these headers that were specified by the API Provider through RAML, or by the developer * through thier configuration. */ $headers = static::get_headers($apicall_obj->get_headers()); $call = array('url' => $apicall_obj->get_url(), 'method' => $apicall_obj->get_method(), 'headers' => $headers, 'body' => $apicall_obj->get_method_params(), 'body-type' => $apicall_obj->get_body_type()); if (\Fuel::$env !== 'production' && \Config::get('engine.dev_disable_live_calls', false) === true) { /** * In dev mode we can disable calls to the remote server. Feel free to change the * dummy response to whatever you'd like to. */ $response = array('status' => 200, 'headers' => array('X-Dev-Mode' => 'Dummy header'), 'body' => array('dummy_key' => 'dummy_val')); return \Utility::format_response(200, $response); } else { /* * We'll see if anyone got a cached entry into our system while we were configuring stuff. * That way we'll save time. */ if (\V1\APIRequest::is_static() && is_array($cached_data = \V1\Call\StaticCall::get_call_cache())) { // Return the response-formatted data from the cached entry. return $cached_data; } $queued = \V1\Socket::forge()->queue_call(\V1\APIRequest::get('api'), $call, $apicall_obj); } // Non-Data Calls grab the request right away. if (\Session::get('data_call', false) === false) { if ($queued === false) { // Server unavailable return \Utility::format_error(503, \Err::SERVER_ERROR, \Lang::get('v1::errors.remote_unavailable')); } // Pull the results. $result = \V1\Socket::forge()->get_results(); if (is_array($result)) { // We only have one call. return $result[\V1\APIRequest::get('api')][0]; } else { // If the request failed with false, it means that all streams timed out. return \Utility::format_error(500); } } $dc_response = array('status' => 200, 'headers' => array(), 'body' => \V1\Constant::QUEUED_CALL); // In Data Call mode we just signify that we've queued the call. return \Utility::format_response(200, $dc_response); }
/** * Run the request * * @param \Raml\SecurityScheme $securityscheme_obj The security scheme to process the call data for * @param \V1\APICall $apicall_obj The APICall object * * @return mixed The object we just completed or an array describing the next step in the security process */ public function run(\Raml\SecurityScheme $securityscheme_obj, \V1\APICall $apicall_obj) { $settings = $securityscheme_obj->getSettings()->asArray(); $credentials = $apicall_obj->get_credentials(); $settings['authorization'] = empty($settings['authorization']) ? 'header' : \Str::lower($settings['authorization']); // Verify that we have the required credentials for the request. if (empty($credentials['OAUTH_CONSUMER_KEY']) || empty($credentials['OAUTH_CONSUMER_SECRET']) || empty($credentials['OAUTH_USER_ID'])) { $this->error = true; return $this; } // Store the proper credentials in the DB. $this->store_credentials($credentials); // Pull data from the cache for the current request, allowing for multiple authentications for the customer. $this->cache_id = hash('sha256', $credentials['OAUTH_CONSUMER_KEY'] . $credentials['OAUTH_CONSUMER_SECRET'] . $credentials['OAUTH_USER_ID']); $credentials = array_replace($this->get_cache(), $credentials); // Where should we set the authorization data? switch ($settings['authorizeLocation']) { case 'header': $authorize_location = OAUTH_AUTH_TYPE_AUTHORIZATION; break; case 'query': $authorize_location = OAUTH_AUTH_TYPE_URI; break; case 'body': $authorize_location = OAUTH_AUTH_TYPE_FORM; break; case 'none': $authorize_location = OAUTH_AUTH_TYPE_NONE; break; } try { // Create the PECL installed OAuth object. $oauth = new \OAuth($credentials['OAUTH_CONSUMER_KEY'], $credentials['OAUTH_CONSUMER_SECRET'], $settings['signatureMethod'], $authorize_location); if (\Fuel::$env !== 'production') { $oauth->enableDebug(); } if (empty($credentials['OAUTH_ACCESS_TOKEN']) || empty($credentials['OAUTH_ACCESS_TOKEN_SECRET'])) { // Get our access token and secret. if (($credentials = $this->get_access_tokens($oauth, $settings, $credentials)) === false) { $this->error = true; return $this; } // Authentication of my second leg (Yup. It's hairy, so it must be mine.) if (!empty($credentials['errors'])) { return $credentials; } } $oauth->setToken($credentials['OAUTH_ACCESS_TOKEN'], $credentials['OAUTH_ACCESS_TOKEN_SECRET']); // Collect parameters to build our signature $params = null; if ($apicall_obj->get_body_type() === 'application/x-www-form-urlencoded') { // If we need to handle string bodies later, we will. if (is_array($apicall_obj->get_method_params())) { $params = http_build_query($apicall_obj->get_method_params(), null, '&', PHP_QUERY_RFC3986) . '&'; } } $params .= http_build_query($apicall_obj->get_query_params(), null, '&') . '&' . ($params .= http_build_query($apicall_obj->get_headers(), null, '&')); $header = $oauth->getRequestHeader($apicall_obj->get_method(), $apicall_obj->get_url(), $params); $apicall_obj->set_header('Authorization', $header); return true; } catch (\OAuthException $e) { // Something went wrong, so destroy the cache so it can get fixed. $this->delete_cache(); // Let the script automatically continue searching for security methods. $this->error = true; return $this; } }