Exemplo n.º 1
0
 /**
  * Add an additional security scheme to the list of schemes the whole API is secured by
  *
  * @param SecurityScheme $securityScheme
  */
 public function addSecuredBy(SecurityScheme $securityScheme)
 {
     $this->securedBy[$securityScheme->getKey()] = $securityScheme;
 }
Exemplo n.º 2
0
 /**
  * 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)
 {
     // If the customer enters an authorization header, then we bail out of the security call.
     if (\V1\APIRequest::is_static() === false && !empty($custom_headers = \V1\APIRequest::get('configure.headers', null)) && !empty($custom_headers['Authorization'])) {
         return true;
     }
     $this->apicall = $apicall_obj;
     /**
      * GATHER THE NEEDED DATA
      */
     $credentials = $apicall_obj->get_credentials();
     $settings = $securityscheme_obj->getSettings();
     $api_def = $apicall_obj->get_api_def();
     $api_url = $apicall_obj->get_url();
     $method = $apicall_obj->get_method();
     $this->credentials = $credentials;
     // Digest URI
     $parsed_url = parse_url($api_url);
     $digest_uri = empty($parsed_url['path']) ? '/' : $parsed_url['path'];
     if (!empty($parsed_url['query'])) {
         $digest_uri .= '?' . $parsed_url['query'];
     }
     $algorithm = empty($settings['algorithm']) ? 'md5' : \Str::lower($settings['algorithm']);
     // Make sure that we have the required data.
     if (empty($credentials['DIGEST_USERNAME']) || empty($credentials['DIGEST_PASSWORD']) || empty($settings['realm'])) {
         $this->error = true;
         return;
     }
     $this->realm = $settings['realm'];
     $this->username = $credentials['DIGEST_USERNAME'];
     // Find our nonce.
     if (empty($credentials['DIGEST_NONCE'])) {
         // Beg for nonces
         $this->parse_www_auth_remote($api_url);
         if (is_array($this->www_data) && !empty($this->www_data['nonce'])) {
             $this->nonce = $this->www_data['nonce'];
         } else {
             $this->error = true;
             return;
         }
     } else {
         $this->nonce = $credentials['DIGEST_NONCE'];
     }
     // We save this value in the DB.
     $credentials['DIGEST_NONCE'] = $this->nonce;
     // Figure out if we've used the current nonce before.
     if (!empty($settings['qop'])) {
         // We may have "auth" or "auth-int" or "auth,auth-int" or "auth-int,auth"
         if (substr_count(\Str::lower($settings['qop']), 'auth-int') > 0) {
             $this->qop = 'auth-int';
         } else {
             $this->qop = 'auth';
         }
         /**
          * We have a qop, so we need to figure out how many times we've sent a request with
          * the current nonce. (Including the current request)
          *
          * @link http://www.ietf.org/rfc/rfc2617.txt
          * Look up "nonce-count"
          */
         if (empty($credentials['DIGEST_NONCE_COUNT'])) {
             $credentials['DIGEST_NONCE_COUNT'] = 0;
         }
         $this->nonce_count = ++$credentials['DIGEST_NONCE_COUNT'];
     }
     // Do we need to send the "opaque" param?
     if (!empty($settings['opaque'])) {
         // It stays the same for the requester forevermore.
         if ($settings['opaque'] === 'same') {
             // We have the value on file. (Dynamic calls only)
             if (!empty($credentials['DIGEST_OPAQUE'])) {
                 $this->opaque = $credentials['DIGEST_OPAQUE'];
             }
         }
         // If it isn't set to "same" or "changes," then we have a static value.
         if ($settings['opaque'] !== 'changes') {
             $this->opaque = $settings['opaque'];
         }
         // We couldn't find the value, so we pull it from the header data of a new request.
         if (empty($this->opaque)) {
             // If we never contacted the remote server, do that now.
             if ($this->www_data === false) {
                 $this->parse_www_auth_remote($api_url);
             }
             if (is_array($this->www_data) && !empty($this->www_data['opaque'])) {
                 $this->opaque = $this->www_data['opaque'];
                 // We'll save it since it'll always be the same.
                 if ($settings['opaque'] === 'same') {
                     $credentials['DIGEST_OPAQUE'] = $this->opaque;
                 }
             } else {
                 // We've called the remote server and it didn't have the data.
                 $this->error = true;
                 return;
             }
         }
     }
     /*
      * Increment our nonce counter for the current request with the nonce. (Pardon me while I go get a
      * bowl of de Chex.)
      */
     $this->nonce_count = dechex($this->nonce_count);
     /**
      * Format the nonce count as specified in section 3.2.2 of the RFC2617.
      * 
      * @link http://www.ietf.org/rfc/rfc2617.txt
      */
     if (($padding = 8 - strlen($this->nonce_count)) > 0) {
         $this->nonce_count = str_repeat(0, $padding) . $this->nonce_count;
     }
     // Reliable client nonce
     $this->cnonce = \Utility::get_nonce();
     /**
      * START COMPILING THE HEADER
      */
     // MD5
     $this->ha1 = md5($credentials['DIGEST_USERNAME'] . ':' . $this->realm . ':' . $credentials['DIGEST_PASSWORD']);
     // MD5-sess
     if ($algorithm === 'md5-sess') {
         $this->ha1 = md5($this->ha1 . ':' . $this->nonce . ':' . $this->cnonce);
     }
     \V1\Keyring::set_credentials($credentials);
     // We've finished for now. We'll configure more just before we send the request.
 }
Exemplo n.º 3
0
 /**
  * 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));
 }
 /**
  * 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;
     }
 }