diff options
Diffstat (limited to 'mod/twitter_api')
| -rw-r--r-- | mod/twitter_api/manifest.xml | 10 | ||||
| -rw-r--r-- | mod/twitter_api/vendors/twitteroauth/OAuth.php | 390 | ||||
| -rw-r--r-- | mod/twitter_api/vendors/twitteroauth/README | 117 | ||||
| -rw-r--r-- | mod/twitter_api/vendors/twitteroauth/twitterOAuth.php | 16 | 
4 files changed, 495 insertions, 38 deletions
| diff --git a/mod/twitter_api/manifest.xml b/mod/twitter_api/manifest.xml index 86bba4b50..3af866bba 100644 --- a/mod/twitter_api/manifest.xml +++ b/mod/twitter_api/manifest.xml @@ -2,7 +2,7 @@  <plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">  	<name>Twitter API</name>  	<author>Core developers</author> -	<version>1.8</version> +	<version>1.8.15</version>  	<description>Allows users to authenticate their Elgg account with Twitter.</description>  	<category>api</category>  	<category>bundled</category> @@ -14,16 +14,16 @@  		<version>1.8</version>  	</requires>  	<requires> -		<type>plugin</type> -		<name>oauth_api</name> -	</requires> -	<requires>  		<type>php_extension</type>  		<name>curl</name>  	</requires>  	<conflicts>  		<type>plugin</type> +		<name>oauth_api</name> +	</conflicts> +	<conflicts> +		<type>plugin</type>  		<name>twitterservice</name>  	</conflicts>  </plugin_manifest> diff --git a/mod/twitter_api/vendors/twitteroauth/OAuth.php b/mod/twitter_api/vendors/twitteroauth/OAuth.php index e132a5bc8..e76304146 100644 --- a/mod/twitter_api/vendors/twitteroauth/OAuth.php +++ b/mod/twitter_api/vendors/twitteroauth/OAuth.php @@ -1,6 +1,12 @@  <?php  // vim: foldmethod=marker +/* Generic exception class + */ +class OAuthException extends Exception { +  // pass +} +  class OAuthConsumer {    public $key;    public $secret; @@ -46,12 +52,56 @@ class OAuthToken {    }  } -class twitterOAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod_HMAC_SHA1 {/*{{{*/ -  function get_name() {/*{{{*/ +/** + * A class for implementing a Signature Method + * See section 9 ("Signing Requests") in the spec + */ +abstract class OAuthSignatureMethod { +  /** +   * Needs to return the name of the Signature Method (ie HMAC-SHA1) +   * @return string +   */ +  abstract public function get_name(); + +  /** +   * Build up the signature +   * NOTE: The output of this function MUST NOT be urlencoded. +   * the encoding is handled in OAuthRequest when the final +   * request is serialized +   * @param OAuthRequest $request +   * @param OAuthConsumer $consumer +   * @param OAuthToken $token +   * @return string +   */ +  abstract public function build_signature($request, $consumer, $token); + +  /** +   * Verifies that a given signature is correct +   * @param OAuthRequest $request +   * @param OAuthConsumer $consumer +   * @param OAuthToken $token +   * @param string $signature +   * @return bool +   */ +  public function check_signature($request, $consumer, $token, $signature) { +    $built = $this->build_signature($request, $consumer, $token); +    return $built == $signature; +  } +} + +/** + * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]  + * where the Signature Base String is the text and the key is the concatenated values (each first  + * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'  + * character (ASCII code 38) even if empty. + *   - Chapter 9.2 ("HMAC-SHA1") + */ +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { +  function get_name() {      return "HMAC-SHA1"; -  }/*}}}*/ +  } -  public function build_signature($request, $consumer, $token) {/*{{{*/ +  public function build_signature($request, $consumer, $token) {      $base_string = $request->get_signature_base_string();      $request->base_string = $base_string; @@ -63,16 +113,111 @@ class twitterOAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod_HMAC_SH      $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);      $key = implode('&', $key_parts); -    return base64_encode( hash_hmac('sha1', $base_string, $key, true)); -  }/*}}}*/ +    return base64_encode(hash_hmac('sha1', $base_string, $key, true)); +  } +} -  public function check_signature(&$request, $consumer, $token, $signature) { -    $built = $this->build_signature($request, $consumer, $token); -    return $built == $signature; +/** + * The PLAINTEXT method does not provide any security protection and SHOULD only be used  + * over a secure channel such as HTTPS. It does not use the Signature Base String. + *   - Chapter 9.4 ("PLAINTEXT") + */ +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { +  public function get_name() { +    return "PLAINTEXT"; +  } + +  /** +   * oauth_signature is set to the concatenated encoded values of the Consumer Secret and  +   * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is  +   * empty. The result MUST be encoded again. +   *   - Chapter 9.4.1 ("Generating Signatures") +   * +   * Please note that the second encoding MUST NOT happen in the SignatureMethod, as +   * OAuthRequest handles this! +   */ +  public function build_signature($request, $consumer, $token) { +    $key_parts = array( +      $consumer->secret, +      ($token) ? $token->secret : "" +    ); + +    $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); +    $key = implode('&', $key_parts); +    $request->base_string = $key; + +    return $key;    } -}/*}}}*/ +} + +/** + * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in  + * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for  + * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a  + * verified way to the Service Provider, in a manner which is beyond the scope of this  + * specification. + *   - Chapter 9.3 ("RSA-SHA1") + */ +abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { +  public function get_name() { +    return "RSA-SHA1"; +  } + +  // Up to the SP to implement this lookup of keys. Possible ideas are: +  // (1) do a lookup in a table of trusted certs keyed off of consumer +  // (2) fetch via http using a url provided by the requester +  // (3) some sort of specific discovery code based on request +  // +  // Either way should return a string representation of the certificate +  protected abstract function fetch_public_cert(&$request); + +  // Up to the SP to implement this lookup of keys. Possible ideas are: +  // (1) do a lookup in a table of trusted certs keyed off of consumer +  // +  // Either way should return a string representation of the certificate +  protected abstract function fetch_private_cert(&$request); + +  public function build_signature($request, $consumer, $token) { +    $base_string = $request->get_signature_base_string(); +    $request->base_string = $base_string; + +    // Fetch the private key cert based on the request +    $cert = $this->fetch_private_cert($request); + +    // Pull the private key ID from the certificate +    $privatekeyid = openssl_get_privatekey($cert); + +    // Sign using the key +    $ok = openssl_sign($base_string, $signature, $privatekeyid); + +    // Release the key resource +    openssl_free_key($privatekeyid); + +    return base64_encode($signature); +  } + +  public function check_signature($request, $consumer, $token, $signature) { +    $decoded_sig = base64_decode($signature); + +    $base_string = $request->get_signature_base_string(); + +    // Fetch the public key cert based on the request +    $cert = $this->fetch_public_cert($request); + +    // Pull the public key ID from the certificate +    $publickeyid = openssl_get_publickey($cert); -class twitterOAuthRequest extends OAuthRequest { +    // Check the computed signature against the one passed in the query +    $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + +    // Release the key resource +    openssl_free_key($publickeyid); + +    return $ok == 1; +  } +} + +class OAuthRequest {    private $parameters;    private $http_method;    private $http_url; @@ -138,7 +283,7 @@ class twitterOAuthRequest extends OAuthRequest {      } -    return new twitterOAuthRequest($http_method, $http_url, $parameters); +    return new OAuthRequest($http_method, $http_url, $parameters);    }    /** @@ -146,16 +291,16 @@ class twitterOAuthRequest extends OAuthRequest {     */    public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {      @$parameters or $parameters = array(); -    $defaults = array("oauth_version" => twitterOAuthRequest::$version, -                      "oauth_nonce" => twitterOAuthRequest::generate_nonce(), -                      "oauth_timestamp" => twitterOAuthRequest::generate_timestamp(), +    $defaults = array("oauth_version" => OAuthRequest::$version, +                      "oauth_nonce" => OAuthRequest::generate_nonce(), +                      "oauth_timestamp" => OAuthRequest::generate_timestamp(),                        "oauth_consumer_key" => $consumer->key);      if ($token)        $defaults['oauth_token'] = $token->key;      $parameters = array_merge($defaults, $parameters); -    return new twitterOAuthRequest($http_method, $http_url, $parameters); +    return new OAuthRequest($http_method, $http_url, $parameters);    }    public function set_parameter($name, $value, $allow_duplicates = true) { @@ -333,6 +478,217 @@ class twitterOAuthRequest extends OAuthRequest {    }  } +class OAuthServer { +  protected $timestamp_threshold = 300; // in seconds, five minutes +  protected $version = '1.0';             // hi blaine +  protected $signature_methods = array(); + +  protected $data_store; + +  function __construct($data_store) { +    $this->data_store = $data_store; +  } + +  public function add_signature_method($signature_method) { +    $this->signature_methods[$signature_method->get_name()] = +      $signature_method; +  } + +  // high level functions + +  /** +   * process a request_token request +   * returns the request token on success +   */ +  public function fetch_request_token(&$request) { +    $this->get_version($request); + +    $consumer = $this->get_consumer($request); + +    // no token required for the initial token request +    $token = NULL; + +    $this->check_signature($request, $consumer, $token); + +    // Rev A change +    $callback = $request->get_parameter('oauth_callback'); +    $new_token = $this->data_store->new_request_token($consumer, $callback); + +    return $new_token; +  } + +  /** +   * process an access_token request +   * returns the access token on success +   */ +  public function fetch_access_token(&$request) { +    $this->get_version($request); + +    $consumer = $this->get_consumer($request); + +    // requires authorized request token +    $token = $this->get_token($request, $consumer, "request"); + +    $this->check_signature($request, $consumer, $token); + +    // Rev A change +    $verifier = $request->get_parameter('oauth_verifier'); +    $new_token = $this->data_store->new_access_token($token, $consumer, $verifier); + +    return $new_token; +  } + +  /** +   * verify an api call, checks all the parameters +   */ +  public function verify_request(&$request) { +    $this->get_version($request); +    $consumer = $this->get_consumer($request); +    $token = $this->get_token($request, $consumer, "access"); +    $this->check_signature($request, $consumer, $token); +    return array($consumer, $token); +  } + +  // Internals from here +  /** +   * version 1 +   */ +  private function get_version(&$request) { +    $version = $request->get_parameter("oauth_version"); +    if (!$version) { +      // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.  +      // Chapter 7.0 ("Accessing Protected Ressources") +      $version = '1.0'; +    } +    if ($version !== $this->version) { +      throw new OAuthException("OAuth version '$version' not supported"); +    } +    return $version; +  } + +  /** +   * figure out the signature with some defaults +   */ +  private function get_signature_method(&$request) { +    $signature_method = +        @$request->get_parameter("oauth_signature_method"); + +    if (!$signature_method) { +      // According to chapter 7 ("Accessing Protected Ressources") the signature-method +      // parameter is required, and we can't just fallback to PLAINTEXT +      throw new OAuthException('No signature method parameter. This parameter is required'); +    } + +    if (!in_array($signature_method, +                  array_keys($this->signature_methods))) { +      throw new OAuthException( +        "Signature method '$signature_method' not supported " . +        "try one of the following: " . +        implode(", ", array_keys($this->signature_methods)) +      ); +    } +    return $this->signature_methods[$signature_method]; +  } + +  /** +   * try to find the consumer for the provided request's consumer key +   */ +  private function get_consumer(&$request) { +    $consumer_key = @$request->get_parameter("oauth_consumer_key"); +    if (!$consumer_key) { +      throw new OAuthException("Invalid consumer key"); +    } + +    $consumer = $this->data_store->lookup_consumer($consumer_key); +    if (!$consumer) { +      throw new OAuthException("Invalid consumer"); +    } + +    return $consumer; +  } + +  /** +   * try to find the token for the provided request's token key +   */ +  private function get_token(&$request, $consumer, $token_type="access") { +    $token_field = @$request->get_parameter('oauth_token'); +    $token = $this->data_store->lookup_token( +      $consumer, $token_type, $token_field +    ); +    if (!$token) { +      throw new OAuthException("Invalid $token_type token: $token_field"); +    } +    return $token; +  } + +  /** +   * all-in-one function to check the signature on a request +   * should guess the signature method appropriately +   */ +  private function check_signature(&$request, $consumer, $token) { +    // this should probably be in a different method +    $timestamp = @$request->get_parameter('oauth_timestamp'); +    $nonce = @$request->get_parameter('oauth_nonce'); + +    $this->check_timestamp($timestamp); +    $this->check_nonce($consumer, $token, $nonce, $timestamp); + +    $signature_method = $this->get_signature_method($request); + +    $signature = $request->get_parameter('oauth_signature'); +    $valid_sig = $signature_method->check_signature( +      $request, +      $consumer, +      $token, +      $signature +    ); + +    if (!$valid_sig) { +      throw new OAuthException("Invalid signature"); +    } +  } + +  /** +   * check that the timestamp is new enough +   */ +  private function check_timestamp($timestamp) { +    if( ! $timestamp ) +      throw new OAuthException( +        'Missing timestamp parameter. The parameter is required' +      ); +     +    // verify that timestamp is recentish +    $now = time(); +    if (abs($now - $timestamp) > $this->timestamp_threshold) { +      throw new OAuthException( +        "Expired timestamp, yours $timestamp, ours $now" +      ); +    } +  } + +  /** +   * check that the nonce is not repeated +   */ +  private function check_nonce($consumer, $token, $nonce, $timestamp) { +    if( ! $nonce ) +      throw new OAuthException( +        'Missing nonce parameter. The parameter is required' +      ); + +    // verify that the nonce is uniqueish +    $found = $this->data_store->lookup_nonce( +      $consumer, +      $token, +      $nonce, +      $timestamp +    ); +    if ($found) { +      throw new OAuthException("Nonce already used: $nonce"); +    } +  } + +} +  class OAuthDataStore {    function lookup_consumer($consumer_key) {      // implement me @@ -514,5 +870,3 @@ class OAuthUtil {      return implode('&', $pairs);    }  } - -?> diff --git a/mod/twitter_api/vendors/twitteroauth/README b/mod/twitter_api/vendors/twitteroauth/README index 33cb91f21..c9a17ce4b 100644 --- a/mod/twitter_api/vendors/twitteroauth/README +++ b/mod/twitter_api/vendors/twitteroauth/README @@ -1,7 +1,114 @@ -Abraham Williams | abraham@poseurte.ch | http://abrah.am | @abraham +TwitterOAuth +------------ -The first PHP library for working with Twitter's OAuth API. +PHP library for working with Twitter's OAuth API. -Documentation: http://wiki.github.com/abraham/twitteroauth/documentation  -Source: http://github.com/abraham/twitteroauth -Twitter: http://apiwiki.twitter.com +Flow Overview +============= + +1. Build TwitterOAuth object using client credentials. +2. Request temporary credentials from Twitter. +3. Build authorize URL for Twitter. +4. Redirect user to authorize URL. +5. User authorizes access and returns from Twitter. +6. Rebuild TwitterOAuth object with client credentials and temporary credentials. +7. Get token credentials from Twitter. +8. Rebuild TwitterOAuth object with client credentials and token credentials. +9. Query Twitter API. + +Terminology +=========== + +The terminology has changed since 0.1.x to better match the draft-hammer-oauth IETF +RFC. You can read that at http://tools.ietf.org/html/draft-hammer-oauth. Some of the +terms will differ from those Twitter uses as well. + +client credentials - Consumer key/secret you get when registering an app with Twitter. +temporary credentials - Previously known as the request token. +token credentials - Previously known as the access token. + +Parameters +========== + +There are a number of parameters you can modify after creating a TwitterOAuth object. + +Switch an existing TwitterOAuth install to use version 1.1 of the API. + +    $connection->$host = "https://api.twitter.com/1.1/"; + +Custom useragent. + +    $connection->useragent = 'Custom useragent string'; + +Verify Twitters SSL certificate. + +    $connection->ssl_verifypeer = TRUE; + +There are several more you can find in TwitterOAuth.php. + +Extended flow using example code +================================ + +To use TwitterOAuth with the Twitter API you need *TwitterOAuth.php*, *OAuth.php* and +client credentials. You can get client credentials by registering your application at +[dev.twitter.com/apps](https://dev.twitter.com/apps). + +Users start out on connect.php which displays the "Sign in with Twitter" image hyperlinked +to redirect.php. This button should be displayed on your homepage in your login section. The +client credentials are saved in config.php as `CONSUMER_KEY` and `CONSUMER_SECRET`. You can +save a static callback URL in the app settings page, in the config file or use a dynamic +callback URL later in step 2. In example use https://example.com/callback.php. + +1) When a user lands on redirect.php we build a new TwitterOAuth object using the client credentials. +If you have your own configuration method feel free to use it instead of config.php. + +    $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET); // Use config.php client credentials +    $connection = new TwitterOAuth('abc890', '123xyz'); + +2) Using the built $connection object you will ask Twitter for temporary credentials. The `oauth_callback` value is required. + +    $temporary_credentials = $connection->getRequestToken(OAUTH_CALLBACK); // Use config.php callback URL. + +3) Now that we have temporary credentials the user has to go to Twitter and authorize the app +to access and updates their data. You can also pass a second parameter of FALSE to not use [Sign +in with Twitter](https://dev.twitter.com/docs/auth/sign-twitter). + +    $redirect_url = $connection->getAuthorizeURL($temporary_credentials); // Use Sign in with Twitter +    $redirect_url = $connection->getAuthorizeURL($temporary_credentials, FALSE); + +4) You will now have a Twitter URL that you must send the user to. + +    https://api.twitter.com/oauth/authenticate?oauth_token=xyz123 + +5) The user is now on twitter.com and may have to login. Once authenticated with Twitter they will +will either have to click on allow/deny, or will be automatically redirected back to the callback. + +6) Now that the user has returned to callback.php and allowed access we need to build a new +TwitterOAuth object using the temporary credentials. + +    $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $_SESSION['oauth_token'], +    $_SESSION['oauth_token_secret']); + +7) Now we ask Twitter for long lasting token credentials. These are specific to the application +and user and will act like password to make future requests. Normally the token credentials would +get saved in your database but for this example we are just using sessions. + +    $token_credentials = $connection->getAccessToken($_REQUEST['oauth_verifier']); + +8) With the token credentials we build a new TwitterOAuth object. + +    $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $token_credentials['oauth_token'], +    $token_credentials['oauth_token_secret']); + +9) And finally we can make requests authenticated as the user. You can GET, POST, and DELETE API +methods. Directly copy the path from the API documentation and add an array of any parameter +you wish to include for the API method such as curser or in_reply_to_status_id. + +    $account = $connection->get('account/verify_credentials'); +    $status = $connection->post('statuses/update', array('status' => 'Text of status here', 'in_reply_to_status_id' => 123456)); +    $status = $connection->delete('statuses/destroy/12345'); + +Contributors +============ + +* [Abraham Williams](https://twitter.com/abraham) - Main developer, current maintainer. diff --git a/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php b/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php index f36e6158d..4c2447c46 100644 --- a/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php +++ b/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php @@ -57,7 +57,7 @@ class TwitterOAuth {     * construct TwitterOAuth object     */    function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) { -    $this->sha1_method = new twitterOAuthSignatureMethod_HMAC_SHA1(); +    $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1();      $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);      if (!empty($oauth_token) && !empty($oauth_token_secret)) {        $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); @@ -72,11 +72,9 @@ class TwitterOAuth {     *     * @returns a key/value array containing oauth_token and oauth_token_secret     */ -  function getRequestToken($oauth_callback = NULL) { +  function getRequestToken($oauth_callback) {      $parameters = array(); -    if (!empty($oauth_callback)) { -      $parameters['oauth_callback'] = $oauth_callback; -    }  +    $parameters['oauth_callback'] = $oauth_callback;       $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);      $token = OAuthUtil::parse_parameters($request);      $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); @@ -108,11 +106,9 @@ class TwitterOAuth {     *                "user_id" => "9436992",     *                "screen_name" => "abraham")     */ -  function getAccessToken($oauth_verifier = FALSE) { +  function getAccessToken($oauth_verifier) {      $parameters = array(); -    if (!empty($oauth_verifier)) { -      $parameters['oauth_verifier'] = $oauth_verifier; -    } +    $parameters['oauth_verifier'] = $oauth_verifier;      $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters);      $token = OAuthUtil::parse_parameters($request);      $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); @@ -179,7 +175,7 @@ class TwitterOAuth {      if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) {        $url = "{$this->host}{$url}.{$this->format}";      } -    $request = twitterOAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters); +    $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);      $request->sign_request($this->sha1_method, $this->consumer, $this->token);      switch ($method) {      case 'GET': | 
