Source for file client.php

Documentation is available at client.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  OAuth1
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM') or die();
  11.  
  12. /**
  13.  * Joomla Platform class for interacting with an OAuth 1.0 and 1.0a server.
  14.  *
  15.  * @package     Joomla.Platform
  16.  * @subpackage  OAuth1
  17.  * @since       13.1
  18.  */
  19. abstract class JOAuth1Client
  20. {
  21.     /**
  22.      * @var    JRegistry  Options for the JOAuth1Client object.
  23.      * @since  13.1
  24.      */
  25.     protected $options;
  26.  
  27.     /**
  28.      * @var    array  Contains access token key, secret and verifier.
  29.      * @since  13.1
  30.      */
  31.     protected $token = array();
  32.  
  33.     /**
  34.      * @var    JHttp  The HTTP client object to use in sending HTTP requests.
  35.      * @since  13.1
  36.      */
  37.     protected $client;
  38.  
  39.     /**
  40.      * @var    JInput The input object to use in retrieving GET/POST data.
  41.      * @since  13.1
  42.      */
  43.     protected $input;
  44.  
  45.     /**
  46.      * @var    JApplicationWeb  The application object to send HTTP headers for redirects.
  47.      * @since  13.1
  48.      */
  49.     protected $application;
  50.  
  51.     /**
  52.      * @var   string  Selects which version of OAuth to use: 1.0 or 1.0a.
  53.      * @since 13.1
  54.      */
  55.     protected $version;
  56.  
  57.     /**
  58.      * Constructor.
  59.      *
  60.      * @param   JRegistry        $options      OAuth1Client options object.
  61.      * @param   JHttp            $client       The HTTP client object.
  62.      * @param   JInput           $input        The input object
  63.      * @param   JApplicationWeb  $application  The application object
  64.      * @param   string           $version      Specify the OAuth version. By default we are using 1.0a.
  65.      *
  66.      * @since   13.1
  67.      */
  68.     public function __construct(JRegistry $options = null, JHttp $client = null, JInput $input = null, JApplicationWeb $application = null,
  69.         $version = null)
  70.     {
  71.         $this->options = isset($options) ? $options : new JRegistry;
  72.         $this->client = isset($client) ? $client : JHttpFactory::getHttp($this->options);
  73.         $this->input = isset($input) ? $input : JFactory::getApplication()->input;
  74.         $this->application = isset($application) ? $application : new JApplicationWeb;
  75.         $this->version = isset($version) ? $version : '1.0a';
  76.     }
  77.  
  78.     /**
  79.      * Method to for the oauth flow.
  80.      *
  81.      * @return  array  Contains access token key, secret and verifier.
  82.      *
  83.      * @since   13.1
  84.      * @throws  DomainException
  85.      */
  86.     public function authenticate()
  87.     {
  88.         // Already got some credentials stored?
  89.         if ($this->token)
  90.         {
  91.             $response = $this->verifyCredentials();
  92.  
  93.             if ($response)
  94.             {
  95.                 return $this->token;
  96.             }
  97.             else
  98.             {
  99.                 $this->token = null;
  100.             }
  101.         }
  102.  
  103.         // Check for callback.
  104.         if (strcmp($this->version, '1.0a') === 0)
  105.         {
  106.             $verifier = $this->input->get('oauth_verifier');
  107.         }
  108.         else
  109.         {
  110.             $verifier = $this->input->get('oauth_token');
  111.         }
  112.  
  113.         if (empty($verifier))
  114.         {
  115.             // Generate a request token.
  116.             $this->_generateRequestToken();
  117.  
  118.             // Authenticate the user and authorise the app.
  119.             $this->_authorise();
  120.         }
  121.  
  122.         // Callback
  123.         else
  124.         {
  125.             $session = JFactory::getSession();
  126.  
  127.             // Get token form session.
  128.             $this->token = array('key' => $session->get('key', null, 'oauth_token'), 'secret' => $session->get('secret', null, 'oauth_token'));
  129.  
  130.             // Verify the returned request token.
  131.             if (strcmp($this->token['key'], $this->input->get('oauth_token')) !== 0)
  132.             {
  133.                 throw new DomainException('Bad session!');
  134.             }
  135.  
  136.             // Set token verifier for 1.0a.
  137.             if (strcmp($this->version, '1.0a') === 0)
  138.             {
  139.                 $this->token['verifier'] = $this->input->get('oauth_verifier');
  140.             }
  141.  
  142.             // Generate access token.
  143.             $this->_generateAccessToken();
  144.  
  145.             // Return the access token.
  146.             return $this->token;
  147.         }
  148.     }
  149.  
  150.     /**
  151.      * Method used to get a request token.
  152.      *
  153.      * @return  void 
  154.      *
  155.      * @since   13.1
  156.      * @throws  DomainException
  157.      */
  158.     private function _generateRequestToken()
  159.     {
  160.         // Set the callback URL.
  161.         if ($this->getOption('callback'))
  162.         {
  163.             $parameters = array(
  164.                 'oauth_callback' => $this->getOption('callback')
  165.             );
  166.         }
  167.         else
  168.         {
  169.             $parameters = array();
  170.         }
  171.  
  172.         // Make an OAuth request for the Request Token.
  173.         $response = $this->oauthRequest($this->getOption('requestTokenURL'), 'POST', $parameters);
  174.  
  175.         parse_str($response->body, $params);
  176.  
  177.         if (strcmp($this->version, '1.0a') === 0 && strcmp($params['oauth_callback_confirmed'], 'true') !== 0)
  178.         {
  179.             throw new DomainException('Bad request token!');
  180.         }
  181.  
  182.         // Save the request token.
  183.         $this->token = array('key' => $params['oauth_token'], 'secret' => $params['oauth_token_secret']);
  184.  
  185.         // Save the request token in session
  186.         $session = JFactory::getSession();
  187.         $session->set('key', $this->token['key'], 'oauth_token');
  188.         $session->set('secret', $this->token['secret'], 'oauth_token');
  189.     }
  190.  
  191.     /**
  192.      * Method used to authorise the application.
  193.      *
  194.      * @return  void 
  195.      *
  196.      * @since   13.1
  197.      */
  198.     private function _authorise()
  199.     {
  200.         $url = $this->getOption('authoriseURL') . '?oauth_token=' . $this->token['key'];
  201.  
  202.         if ($this->getOption('scope'))
  203.         {
  204.             $scope = is_array($this->getOption('scope')) ? implode(' ', $this->getOption('scope')) : $this->getOption('scope');
  205.             $url .= '&scope=' . urlencode($scope);
  206.         }
  207.  
  208.         if ($this->getOption('sendheaders'))
  209.         {
  210.             $this->application->redirect($url);
  211.         }
  212.     }
  213.  
  214.     /**
  215.      * Method used to get an access token.
  216.      *
  217.      * @return  void 
  218.      *
  219.      * @since   13.1
  220.      */
  221.     private function _generateAccessToken()
  222.     {
  223.         // Set the parameters.
  224.         $parameters = array(
  225.             'oauth_token' => $this->token['key']
  226.         );
  227.  
  228.         if (strcmp($this->version, '1.0a') === 0)
  229.         {
  230.             $parameters = array_merge($parameters, array('oauth_verifier' => $this->token['verifier']));
  231.         }
  232.  
  233.         // Make an OAuth request for the Access Token.
  234.         $response = $this->oauthRequest($this->getOption('accessTokenURL'), 'POST', $parameters);
  235.  
  236.         parse_str($response->body, $params);
  237.  
  238.         // Save the access token.
  239.         $this->token = array('key' => $params['oauth_token'], 'secret' => $params['oauth_token_secret']);
  240.     }
  241.  
  242.     /**
  243.      * Method used to make an OAuth request.
  244.      *
  245.      * @param   string  $url         The request URL.
  246.      * @param   string  $method      The request method.
  247.      * @param   array   $parameters  Array containing request parameters.
  248.      * @param   mixed   $data        The POST request data.
  249.      * @param   array   $headers     An array of name-value pairs to include in the header of the request
  250.      *
  251.      * @return  JHttpResponse 
  252.      *
  253.      * @since   13.1
  254.      * @throws  DomainException
  255.      */
  256.     public function oauthRequest($url, $method, $parameters, $data = array(), $headers = array())
  257.     {
  258.         // Set the parameters.
  259.         $defaults = array(
  260.             'oauth_consumer_key' => $this->getOption('consumer_key'),
  261.             'oauth_signature_method' => 'HMAC-SHA1',
  262.             'oauth_version' => '1.0',
  263.             'oauth_nonce' => $this->generateNonce(),
  264.             'oauth_timestamp' => time()
  265.         );
  266.  
  267.         $parameters = array_merge($parameters, $defaults);
  268.  
  269.         // Do not encode multipart parameters. Do not include $data in the signature if $data is not array.
  270.         if (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') !== false || !is_array($data))
  271.         {
  272.             $oauth_headers = $parameters;
  273.         }
  274.         else
  275.         {
  276.             // Use all parameters for the signature.
  277.             $oauth_headers = array_merge($parameters, $data);
  278.         }
  279.  
  280.         // Sign the request.
  281.         $oauth_headers = $this->_signRequest($url, $method, $oauth_headers);
  282.  
  283.         // Get parameters for the Authorisation header.
  284.         if (is_array($data))
  285.         {
  286.             $oauth_headers = array_diff_key($oauth_headers, $data);
  287.         }
  288.  
  289.         // Send the request.
  290.         switch ($method)
  291.         {
  292.             case 'GET':
  293.                 $url = $this->toUrl($url, $data);
  294.                 $response = $this->client->get($url, array('Authorization' => $this->_createHeader($oauth_headers)));
  295.                 break;
  296.             case 'POST':
  297.                 $headers = array_merge($headers, array('Authorization' => $this->_createHeader($oauth_headers)));
  298.                 $response = $this->client->post($url, $data, $headers);
  299.                 break;
  300.             case 'PUT':
  301.                 $headers = array_merge($headers, array('Authorization' => $this->_createHeader($oauth_headers)));
  302.                 $response = $this->client->put($url, $data, $headers);
  303.                 break;
  304.             case 'DELETE':
  305.                 $headers = array_merge($headers, array('Authorization' => $this->_createHeader($oauth_headers)));
  306.                 $response = $this->client->delete($url, $headers);
  307.                 break;
  308.         }
  309.  
  310.         // Validate the response code.
  311.         $this->validateResponse($url, $response);
  312.  
  313.         return $response;
  314.     }
  315.  
  316.     /**
  317.      * Method to validate a response.
  318.      *
  319.      * @param   string         $url       The request URL.
  320.      * @param   JHttpResponse  $response  The response to validate.
  321.      *
  322.      * @return  void 
  323.      *
  324.      * @since   13.1
  325.      * @throws  DomainException
  326.      */
  327.     abstract public function validateResponse($url, $response);
  328.  
  329.     /**
  330.      * Method used to create the header for the POST request.
  331.      *
  332.      * @param   array  $parameters  Array containing request parameters.
  333.      *
  334.      * @return  string  The header.
  335.      *
  336.      * @since   13.1
  337.      */
  338.     private function _createHeader($parameters)
  339.     {
  340.         $header = 'OAuth ';
  341.  
  342.         foreach ($parameters as $key => $value)
  343.         {
  344.             if (!strcmp($header, 'OAuth '))
  345.             {
  346.                 $header .= $key . '="' . $this->safeEncode($value) . '"';
  347.             }
  348.             else
  349.             {
  350.                 $header .= ', ' . $key . '="' . $value . '"';
  351.             }
  352.         }
  353.  
  354.         return $header;
  355.     }
  356.  
  357.     /**
  358.      * Method to create the URL formed string with the parameters.
  359.      *
  360.      * @param   string  $url         The request URL.
  361.      * @param   array   $parameters  Array containing request parameters.
  362.      *
  363.      * @return  string  The formed URL.
  364.      *
  365.      * @since   13.1
  366.      */
  367.     public function toUrl($url, $parameters)
  368.     {
  369.         foreach ($parameters as $key => $value)
  370.         {
  371.             if (is_array($value))
  372.             {
  373.                 foreach ($value as $v)
  374.                 {
  375.                     if (strpos($url, '?') === false)
  376.                     {
  377.                         $url .= '?' . $key . '=' . $v;
  378.                     }
  379.                     else
  380.                     {
  381.                         $url .= '&' . $key . '=' . $v;
  382.                     }
  383.                 }
  384.             }
  385.             else
  386.             {
  387.                 if (strpos($value, ' ') !== false)
  388.                 {
  389.                     $value = $this->safeEncode($value);
  390.                 }
  391.  
  392.                 if (strpos($url, '?') === false)
  393.                 {
  394.                     $url .= '?' . $key . '=' . $value;
  395.                 }
  396.                 else
  397.                 {
  398.                     $url .= '&' . $key . '=' . $value;
  399.                 }
  400.             }
  401.         }
  402.  
  403.         return $url;
  404.     }
  405.  
  406.     /**
  407.      * Method used to sign requests.
  408.      *
  409.      * @param   string  $url         The URL to sign.
  410.      * @param   string  $method      The request method.
  411.      * @param   array   $parameters  Array containing request parameters.
  412.      *
  413.      * @return  array 
  414.      *
  415.      * @since   13.1
  416.      */
  417.     private function _signRequest($url, $method, $parameters)
  418.     {
  419.         // Create the signature base string.
  420.         $base = $this->_baseString($url, $method, $parameters);
  421.  
  422.         $parameters['oauth_signature'] = $this->safeEncode(
  423.             base64_encode(
  424.                 hash_hmac('sha1', $base, $this->_prepareSigningKey(), true)
  425.                 )
  426.             );
  427.  
  428.         return $parameters;
  429.     }
  430.  
  431.     /**
  432.      * Prepare the signature base string.
  433.      *
  434.      * @param   string  $url         The URL to sign.
  435.      * @param   string  $method      The request method.
  436.      * @param   array   $parameters  Array containing request parameters.
  437.      *
  438.      * @return  string  The base string.
  439.      *
  440.      * @since   13.1
  441.      */
  442.     private function _baseString($url, $method, $parameters)
  443.     {
  444.         // Sort the parameters alphabetically
  445.         uksort($parameters, 'strcmp');
  446.  
  447.         // Encode parameters.
  448.         foreach ($parameters as $key => $value)
  449.         {
  450.             $key = $this->safeEncode($key);
  451.  
  452.             if (is_array($value))
  453.             {
  454.                 foreach ($value as $v)
  455.                 {
  456.                     $v = $this->safeEncode($v);
  457.                     $kv[] = "{$key}={$v}";
  458.                 }
  459.             }
  460.             else
  461.             {
  462.                 $value = $this->safeEncode($value);
  463.                 $kv[] = "{$key}={$value}";
  464.             }
  465.         }
  466.         // Form the parameter string.
  467.         $params = implode('&', $kv);
  468.  
  469.         // Signature base string elements.
  470.         $base = array(
  471.             $method,
  472.             $url,
  473.             $params
  474.             );
  475.  
  476.         // Return the base string.
  477.         return implode('&', $this->safeEncode($base));
  478.     }
  479.  
  480.     /**
  481.      * Encodes the string or array passed in a way compatible with OAuth.
  482.      * If an array is passed each array value will will be encoded.
  483.      *
  484.      * @param   mixed  $data  The scalar or array to encode.
  485.      *
  486.      * @return  string  $data encoded in a way compatible with OAuth.
  487.      *
  488.      * @since   13.1
  489.      */
  490.     public function safeEncode($data)
  491.     {
  492.         if (is_array($data))
  493.         {
  494.             return array_map(array($this, 'safeEncode'), $data);
  495.         }
  496.         elseif (is_scalar($data))
  497.         {
  498.             return str_ireplace(
  499.                 array('+', '%7E'),
  500.                 array(' ', '~'),
  501.                 rawurlencode($data)
  502.                 );
  503.         }
  504.         else
  505.         {
  506.             return '';
  507.         }
  508.     }
  509.  
  510.     /**
  511.      * Method used to generate the current nonce.
  512.      *
  513.      * @return  string  The current nonce.
  514.      *
  515.      * @since   13.1
  516.      */
  517.     public static function generateNonce()
  518.     {
  519.         $mt = microtime();
  520.         $rand = mt_rand();
  521.  
  522.         // The md5s look nicer than numbers.
  523.         return md5($mt . $rand);
  524.     }
  525.  
  526.     /**
  527.      * Prepares the OAuth signing key.
  528.      *
  529.      * @return  string  The prepared signing key.
  530.      *
  531.      * @since   13.1
  532.      */
  533.     private function _prepareSigningKey()
  534.     {
  535.         return $this->safeEncode($this->getOption('consumer_secret')) . '&' . $this->safeEncode(($this->token) ? $this->token['secret'] : '');
  536.     }
  537.  
  538.     /**
  539.      * Returns an HTTP 200 OK response code and a representation of the requesting user if authentication was successful;
  540.      * returns a 401 status code and an error message if not.
  541.      *
  542.      * @return  array  The decoded JSON response
  543.      *
  544.      * @since   13.1
  545.      */
  546.     abstract public function verifyCredentials();
  547.  
  548.     /**
  549.      * Get an option from the JOauth1aClient instance.
  550.      *
  551.      * @param   string  $key  The name of the option to get
  552.      *
  553.      * @return  mixed  The option value
  554.      *
  555.      * @since   13.1
  556.      */
  557.     public function getOption($key)
  558.     {
  559.         return $this->options->get($key);
  560.     }
  561.  
  562.     /**
  563.      * Set an option for the JOauth1aClient instance.
  564.      *
  565.      * @param   string  $key    The name of the option to set
  566.      * @param   mixed   $value  The option value to set
  567.      *
  568.      * @return  JOAuth1Client  This object for method chaining
  569.      *
  570.      * @since   13.1
  571.      */
  572.     public function setOption($key, $value)
  573.     {
  574.         $this->options->set($key, $value);
  575.  
  576.         return $this;
  577.     }
  578.  
  579.     /**
  580.      * Get the oauth token key or secret.
  581.      *
  582.      * @return  array  The oauth token key and secret.
  583.      *
  584.      * @since   13.1
  585.      */
  586.     public function getToken()
  587.     {
  588.         return $this->token;
  589.     }
  590.  
  591.     /**
  592.      * Set the oauth token.
  593.      *
  594.      * @param   array  $token  The access token key and secret.
  595.      *
  596.      * @return  JOAuth1Client  This object for method chaining.
  597.      *
  598.      * @since   13.1
  599.      */
  600.     public function setToken($token)
  601.     {
  602.         $this->token = $token;
  603.  
  604.         return $this;
  605.     }
  606. }

Documentation generated on Tue, 19 Nov 2013 14:55:53 +0100 by phpDocumentor 1.4.3