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 nullJHttp $client nullJInput $input nullJApplicationWeb $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'=== && 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($parametersarray('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($urlarray('Authorization' => $this->_createHeader($oauth_headers)));
  295.                 break;
  296.             case 'POST':
  297.                 $headers array_merge($headersarray('Authorization' => $this->_createHeader($oauth_headers)));
  298.                 $response $this->client->post($url$data$headers);
  299.                 break;
  300.             case 'PUT':
  301.                 $headers array_merge($headersarray('Authorization' => $this->_createHeader($oauth_headers)));
  302.                 $response $this->client->put($url$data$headers);
  303.                 break;
  304.             case 'DELETE':
  305.                 $headers array_merge($headersarray('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