/** * Touch up some settings after we've grabbed the response from the remote server. * * @param array $response The array of response data from \V1\Socket * @return array The $response array with any needed alterations */ public function after_response(array $response) { // If the authentication was invalid, the server will spit out a 401 response, and give us a new nonce. if ($response['status'] === 401 && !empty($response['headers']['WWW-Authenticate'])) { if (is_array($www_auth = $this->parse_www_auth($response['headers'])) && !empty($www_auth['nonce'])) { /* * Set the new nonce, and reset the number of calls that we've made with the nonce, thus * defeating the whole point of a number once, as per the RFC2617. Gotta love people who * don't know how to program! (Sarcasm) */ $this->credentials['DIGEST_NONCE'] = $www_auth['nonce']; $this->credentials['DIGEST_NONCE_COUNT'] = 0; \V1\Keyring::set_credentials($this->credentials); } } return $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(); $credentials = $apicall_obj->get_credentials(); // Save the credentials \V1\Keyring::set_credentials($credentials); /** * By default we'll return the response from the authentication request so that it's meaningful. * However, in doing so, we'll need to block the main request, so developers may set this flag * to ignore the authentication, signifying that they've already got the information they needed * from it. * * NOTE: This security method is meant as a basic way to catch security methods we otherwise * haven't implemented in our system. Take it for what it's worth. * * @TODO When using this security method, skip processing the APICall object for a speedup. */ if (!empty($credentials['CUSTOM_IGNORE_AUTH'])) { return true; } // Remove unused credentials so as not to replace bad variables in the template. foreach ($credentials as $variable => $entry) { if (strpos($variable, 'CUSTOM_') !== 0) { unset($credentials[$variable]); } } // We need the method or we'll fail the call. if (empty($settings['method'])) { $this->error = true; return $this; } // Normalize the data into arrays. $described_by = $securityscheme_obj->getDescribedBy(); $headers = $this->get_param_array($described_by->getHeaders()); $query_params = $this->get_param_array($described_by->getQueryParameters()); $bodies = $described_by->getBodies(); $method = \Str::upper($settings['method']); $url = $settings['url']; // Grab the body if we have one, and the method supports one. $body = null; $body_type = null; if (count($bodies) > 0 && !in_array($method, array('GET', 'HEAD'))) { reset($bodies); $body_type = key($bodies); $body = $bodies[$body_type]->getExamples()[0]; } /** * NOTE: These replacements may ruin the formatting or allow for people to inject data into them. * API Providers should be aware of that possibility. * * @TODO In the future, we can consider implementing checking to verify that people aren't sending * crap data through the system. */ $headers = $this->remove_cr_and_lf($this->replace_variables($headers, $credentials)); $query_params = $this->remove_cr_and_lf($this->replace_variables($query_params, $credentials)); $body = $this->replace_variables($body, $credentials); if (!empty($query_params)) { $query_string = http_build_query($query_params, null, '&'); if (strpos($url, '?') === false) { $url .= '?' . $query_string; } else { $url .= '&' . $query_string; } } /** * RUNCLE RICK'S RAD RUN CALLS (The second coming!) */ $curl = \Remote::forge($url, 'curl', $method); // Set the headers $headers = \V1\RunCall::get_headers($headers); foreach ($headers as $header_name => $header_value) { $curl->set_header($header_name, $header_value); } // Return the headers $curl->set_option(CURLOPT_HEADER, true); // If we need a body, set that. if (!empty($body) && !in_array($method, array('GET', 'HEAD'))) { $curl->set_header('Content-Type', $body_type); $curl->set_params($body); } // Run the request try { $response = $curl->execute()->response(); } catch (\RequestStatusException $e) { $response = \Remote::get_response($curl); } catch (\RequestException $e) { $this->error = true; return $this; } // Set the usage stats, and format the response return \V1\Socket::prepare_response(array('status' => $response->status, 'headers' => $response->headers, 'body' => $response->body)); }
/** * Select the proper credentials to store in the DB. * * @param array $credentials The array of credentials to store */ private function store_credentials(array $credentials) { // We'll pitch any OAuth 1.0a credentials so that we can choose what to store later. $set_credentials = $credentials; foreach ($set_credentials as $variable => $value) { if (strpos($variable, 'OAUTH_') !== false) { unset($set_credentials[$variable]); } } // We'll save the consumer key and secret, plus the callback URL if we have one. $set_credentials = array('OAUTH_CONSUMER_KEY' => $credentials['OAUTH_CONSUMER_KEY'], 'OAUTH_CONSUMER_SECRET' => $credentials['OAUTH_CONSUMER_SECRET'], 'OAUTH_USER_ID' => $credentials['OAUTH_USER_ID']); \V1\Keyring::set_credentials($set_credentials); }