jenkins-bot has submitted this change and it was merged.

Change subject: Add OAuth library
......................................................................


Add OAuth library

Add an OAuth client library extracted from
https://code.google.com/p/oauth/ and an OAuth client extracted from
https://github.com/Stype/mwoauth-php.

Change-Id: Id8222bbe9960942f94fd67d7fbc3290cb0105245
---
A src/OAuth/Client.php
A src/OAuth/ClientConfig.php
A src/OAuth/Consumer.php
A src/OAuth/Exception.php
A src/OAuth/Request.php
A src/OAuth/SignatureMethod.php
A src/OAuth/SignatureMethod/HmacSha1.php
A src/OAuth/SignatureMethod/Plaintext.php
A src/OAuth/SignatureMethod/RsaSha1.php
A src/OAuth/Token.php
A src/OAuth/Util.php
A tests/OAuth/ConsumerTest.php
A tests/OAuth/RequestTest.php
A tests/OAuth/SignatureMethod/HmacSha1Test.php
A tests/OAuth/SignatureMethod/PlaintextTest.php
A tests/OAuth/SignatureMethod/RsaSha1Test.php
A tests/OAuth/TokenTest.php
17 files changed, 2,516 insertions(+), 0 deletions(-)

Approvals:
  CSteipp: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/src/OAuth/Client.php b/src/OAuth/Client.php
new file mode 100644
index 0000000..133bfbe
--- /dev/null
+++ b/src/OAuth/Client.php
@@ -0,0 +1,358 @@
+<?php
+/**
+ * @section LICENSE
+ * This file is part of Wikimedia Slim application library
+ *
+ * Wikimedia Slim application library is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * Wikimedia Slim application library is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Wikimedia Grants Review application.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @copyright © 2015 Chris Steipp, Wikimedia Foundation and contributors.
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+use Wikimedia\Slimapp\OAuth\SignatureMethod\HmacSha1;
+use Exception;
+
+/**
+ * MediaWiki OAuth client.
+ */
+class Client implements LoggerAwareInterface {
+
+       /**
+        * @var LoggerInterface $logger
+        */
+       protected $logger;
+
+       /**
+        * @var ClientConfig $config
+        */
+       private $config;
+
+       /**
+        * Any extra params in the call that need to be signed
+        * @var array $extraParams
+        */
+       private $extraParams = array();
+
+       /**
+        * url, defaults to oob
+        * @var string $callbackUrl
+        */
+       private $callbackUrl = 'oob';
+
+       /**
+        * Track the last random nonce generated by the OAuth lib, used to 
verify
+        * /identity response isn't a replay
+        * @var string $lastNonce
+        */
+       private $lastNonce;
+
+       /**
+        * @param ClientConfig $config
+        * @param LoggerInterface $logger
+        */
+       function __construct(
+               ClientConfig $config,
+               LoggerInterface $logger = null
+       ) {
+               $this->config = $config;
+               $this->logger = $logger ?: new NullLogger();
+       }
+
+       /**
+        * @param LoggerInterface $logger
+        */
+       public function setLogger( LoggerInterface $logger ) {
+               $this->logger = $logger;
+       }
+
+       /**
+        * @param string $url
+        * @param string $key
+        * @param string $secret
+        * @return MediaWiki
+        */
+       public static function newFromKeyAndSecret( $url, $key, $secret ) {
+               $config = new ClientConfig( $url, true, true );
+               $config->setConsumer( new Consumer( $key, $secret ) );
+               return new static( $config );
+       }
+
+       /**
+        * @param string $key
+        * @param string $value
+        */
+       public function setExtraParam( $key, $value ) {
+               $this->extraParams[$key] = $value;
+       }
+
+       /**
+        * @param array $params
+        */
+       public function setExtraParams( array $params ) {
+               $this->extraParams = $params;
+       }
+
+       /**
+        * @param string $url
+        */
+       public function setCallback( $url ) {
+               $this->callbackUrl = $url;
+       }
+
+       /**
+        * First part of 3-legged OAuth, get the request Token.
+        * Redirect your authorizing users to the redirect url, and keep
+        * track of the request token since you need to pass it into complete()
+        *
+        * @return array (redirect, request/temp token)
+        */
+       public function initiate() {
+               $initUrl = $this->config->endpointURL .
+                       '/initiate&format=json&oauth_callback=' .
+                       urlencode( $this->callbackUrl );
+               $data = $this->makeOAuthCall( null, $initUrl );
+               $return = json_decode( $data );
+               if ( $return->oauth_callback_confirmed !== 'true' ) {
+                       throw new Exception( "Callback wasn't confirmed" );
+               }
+               $requestToken = new Token( $return->key, $return->secret );
+               $url = $this->config->redirURL ?:
+                       $this->config->endpointURL . "/authorize&";
+               $url .= 
"oauth_token={$requestToken->key}&oauth_consumer_key={$this->config->consumer->key}";
+               return array( $url, $requestToken );
+       }
+
+       /**
+        * The final leg of the OAuth handshake. Exchange the request Token from
+        * initiate() and the verification code that the user submitted back to 
you
+        * for an access token, which you'll use for all API calls.
+        *
+        * @param Token $requestToken Authorization code sent to the callback 
url
+        * @param string Temp/request token obtained from initiate, or null if 
this
+        *     object was used and the token is already set.
+        * @return Token The access token
+        */
+       public function complete( Token $requestToken, $verifyCode ) {
+               $tokenUrl = $this->config->endpointURL . '/token&format=json';
+               $this->setExtraParam( 'oauth_verifier', $verifyCode );
+               $data = $this->makeOAuthCall( $requestToken, $tokenUrl );
+               $return = json_decode( $data );
+               $accessToken = new Token( $return->key, $return->secret );
+               // Cleanup after ourselves
+               $this->setExtraParams = array();
+               return $accessToken;
+       }
+
+       /**
+        * Optional step. This call the MediaWiki specific /identify method, 
which
+        * returns a signed statement of the authorizing user's identity. Use 
this
+        * if you are authenticating users in your application, and you need to
+        * know their username, groups, rights, etc in MediaWiki.
+        *
+        * @param Token $accessToken Access token from complete()
+        * @return object containing attributes of the user
+        */
+       public function identify( Token $accessToken ) {
+               $identifyUrl = $this->config->endpointURL . '/identify';
+               $data = $this->makeOAuthCall( $accessToken, $identifyUrl );
+               $identity = $this->decodeJWT( $data, 
$this->config->consumer->secret );
+               if ( !$this->validateJWT(
+                       $identity,
+                       $this->config->consumer->key,
+                       $this->config->canonicalServerUrl,
+                       $this->lastNonce
+               ) ) {
+                       throw new Exception( "JWT didn't validate" );
+               }
+               return $identity;
+       }
+
+       /**
+        * Make a signed request to MediaWiki
+        *
+        * @param Token $token additional token to use in signature, besides
+        *     the consumer token. In most cases, this will be the access token 
you
+        *     got from complete(), but we set it to the request token when
+        *     finishing the handshake.
+        * @param string $url URL to call
+        * @param bool $isPost true if this should be a POST request
+        * @param array $postFields POST parameters, only if $isPost is also 
true
+        * @return string Body from the curl request
+        */
+       public function makeOAuthCall(
+               /*Token*/ $token, $url, $isPost = false, array $postFields = 
null
+       ) {
+               $params = array();
+               // Get any params from the url
+               if ( strpos( $url, '?' ) ) {
+                       $parsed = parse_url( $url );
+                       parse_str( $parsed['query'], $params );
+               }
+               $params += $this->extraParams;
+               if ( $isPost && $postFields ) {
+                       $params += $postFields;
+               }
+               $method = $isPost ? 'POST' : 'GET';
+               $req = Request::fromConsumerAndToken(
+                       $this->config->consumer,
+                       $token,
+                       $method,
+                       $url,
+                       $params
+               );
+               $req->signRequest(
+                       new HmacSha1(),
+                       $this->config->consumer,
+                       $token
+               );
+               $this->lastNonce = $req->getParameter( 'oauth_nonce' );
+               return $this->makeCurlCall(
+                       $url,
+                       $req->toHeader(),
+                       $isPost,
+                       $postFields,
+                       $this->config
+               );
+       }
+
+       /**
+        * @param string $url
+        * @param array $headers
+        * @param bool $isPost
+        * @param array $postFields
+        * @return string
+        */
+       private function makeCurlCall(
+               $url, $headers, $isPost, array $postFields = null
+       ) {
+               $ch = curl_init();
+               curl_setopt( $ch, CURLOPT_URL, (string) $url );
+               curl_setopt( $ch, CURLOPT_HEADER, 0 );
+               curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
+               curl_setopt( $ch, CURLOPT_HTTPHEADER, array( $headers ) );
+               if ( $isPost ) {
+                       curl_setopt( $ch, CURLOPT_POST, true );
+                       curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query( 
$postFields ) );
+               }
+               if ( $this->config->useSSL ) {
+                       curl_setopt( $ch, CURLOPT_PORT, 443 );
+               }
+               if ( $this->config->verifySSL ) {
+                       curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true );
+                       curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
+               } else {
+                       curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
+                       curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
+               }
+               $data = curl_exec( $ch );
+               if ( !$data ) {
+                       throw new Exception( 'Curl error: ' . curl_error( $ch ) 
);
+               }
+               return $data;
+       }
+
+       /**
+        * @param string $JWT Json web token
+        * @param string $secret
+        * @return object
+        */
+       private function decodeJWT( $JWT, $secret ) {
+               list( $headb64, $bodyb64, $sigb64 ) = explode( '.', $JWT );
+               $header = json_decode( $this->urlsafeB64Decode( $headb64 ) );
+               $payload = json_decode( $this->urlsafeB64Decode( $bodyb64 ) );
+               $sig = $this->urlsafeB64Decode( $sigb64 );
+               // MediaWiki will only use sha256 hmac (HS256) for now. This 
check
+               // makes sure an attacker doesn't return a JWT with 'none' 
signature
+               // type.
+               $expectSig = hash_hmac(
+                       'sha256', "{$headb64}.{$bodyb64}", $secret, true
+               );
+               if ( $header->alg !== 'HS256' || !$this->compareHash( $sig, 
$expectSig ) ) {
+                       throw new Exception( "Invalid JWT signature from 
/identify." );
+               }
+               return $payload;
+       }
+
+       /**
+        * @param object $identity
+        * @param string $consumerKey
+        * @param string $expectedConnonicalServer
+        * @param string $nonce
+        */
+       protected function validateJWT(
+               $identity, $consumerKey, $expectedConnonicalServer, $nonce
+       ) {
+               // Verify the issuer is who we expect (server sends 
$wgCanonicalServer)
+               if ( $identity->iss !== $expectedConnonicalServer ) {
+                       $this->logger->info(
+                               "Invalid issuer '{$identity->iss}': expected 
'{$expectedConnonicalServer}'" );
+                       return false;
+               }
+               // Verify we are the intended audience
+               if ( $identity->aud !== $consumerKey ) {
+                       $this->logger->info( "Invalid audience 
'{$identity->aud}': expected '{$consumerKey}'" );
+                       return false;
+               }
+               // Verify we are within the time limits of the token. Issued at 
(iat)
+               // should be in the past, Expiration (exp) should be in the 
future.
+               $now = time();
+               if ( $identity->iat > $now || $identity->exp < $now ) {
+                       $this->logger->info(
+                               "Invalid times issued='{$identity->iat}', " .
+                               "expires='{$identity->exp}', now='{$now}'"
+                       );
+                       return false;
+               }
+               // Verify we haven't seen this nonce before, which would 
indicate a replay attack
+               if ( $identity->nonce !== $nonce ) {
+                       $this->logger->info( "Invalid nonce 
'{$identity->nonce}': expected '{$nonce}'" );
+                       return false;
+               }
+               return true;
+       }
+
+       /**
+        * @param string $input
+        * @return string
+        */
+       private function urlsafeB64Decode( $input ) {
+               $remainder = strlen( $input ) % 4;
+               if ( $remainder ) {
+                       $padlen = 4 - $remainder;
+                       $input .= str_repeat( '=', $padlen );
+               }
+               return base64_decode( strtr( $input, '-_', '+/' ) );
+       }
+
+       /**
+        * Constant time comparison
+        * @param string $hash1
+        * @param string $hash2
+        * @return bool
+        */
+       private function compareHash( $hash1, $hash2 ) {
+               $result = strlen( $hash1 ) ^ strlen( $hash2 );
+               $len = min( strlen( $hash1 ), strlen( $hash2 ) ) - 1;
+               for ( $i = 0; $i < $len; $i++ ) {
+                       $result |= ord( $hash1{$i} ) ^ ord( $hash2{$i} );
+               }
+               return $result == 0;
+       }
+}
diff --git a/src/OAuth/ClientConfig.php b/src/OAuth/ClientConfig.php
new file mode 100644
index 0000000..f9f143a
--- /dev/null
+++ b/src/OAuth/ClientConfig.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * @section LICENSE
+ * This file is part of Wikimedia Slim application library
+ *
+ * Wikimedia Slim application library is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * Wikimedia Slim application library is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Wikimedia Grants Review application.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @copyright © 2015 Chris Steipp, Wikimedia Foundation and contributors.
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+/**
+ * MediaWiki OAuth client configuration
+ */
+class ClientConfig {
+       /**
+        * Url to the OAuth special page
+        * @var string $endpointURL
+        */
+       public $endpointURL;
+
+       /**
+        * Canonical server url, used to check /identify's iss.
+        * A default value will be created based on the provided $endpointURL.
+        * @var string $canonicalServerUrl
+        */
+       public $canonicalServerUrl;
+
+       /**
+        * Url that the user is sent to. Can be different from $endpointURL to
+        * play nice with MobileFrontend, etc.
+        * @var string|null $redirURL
+        */
+       public $redirURL = null;
+
+       /**
+        * Use https when calling the server.
+        * @var bool $useSSL
+        */
+       public $useSSL;
+
+       /**
+        * If you're testing against a server with self-signed certificates, you
+        * can turn this off but don't do this in production.
+        * @var bool $verifySSL
+        */
+       public $verifySSL;
+
+       /**
+        * @var Consumer|null $consumer
+        */
+       public $consumer = null;
+
+       /**
+        * @param string $url OAuth endpoint URL
+        * @param bool $verifySSL
+        */
+       function __construct( $url, $verifySSL = true ) {
+               $this->endpointURL = $url;
+               $this->verifySSL = $verifySSL;
+
+               $parts = parse_url( $url );
+               $this->useSSL = $parts['scheme'] === 'https';
+               $this->canonicalServerUrl = 
"{$parts['scheme']}://{$parts['host']}" .
+                       ( isset( $parts['port'] ) ? ':' . $parts['port'] : '' );
+       }
+
+       /**
+        * @param string $redirURL
+        * @return ClientConfig Self, for method chaining
+        */
+       public function setRedirUrl( $redirURL ) {
+               $this->redirURL = $redirURL;
+               return $this;
+       }
+
+       /**
+        * @param Consumer $consumer
+        * @return ClientConfig Self, for method chaining
+        */
+       public function setConsumer( Consumer $consumer ) {
+               $this->consumer = $consumer;
+               return $this;
+       }
+}
diff --git a/src/OAuth/Consumer.php b/src/OAuth/Consumer.php
new file mode 100644
index 0000000..54310f5
--- /dev/null
+++ b/src/OAuth/Consumer.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+/**
+ * Data type that represents the identity of the Consumer via its shared
+ * secret with the ServiceProvider.
+ */
+class Consumer {
+       /**
+        * @var string $key
+        */
+       public $key;
+
+       /**
+        * @var string $secret
+        */
+       public $secret;
+
+       function __construct( $key, $secret ) {
+               $this->key = $key;
+               $this->secret = $secret;
+       }
+
+       function __toString() {
+               return __CLASS__ . "[key={$this->key},secret={$this->secret}]";
+       }
+}
diff --git a/src/OAuth/Exception.php b/src/OAuth/Exception.php
new file mode 100644
index 0000000..bc09241
--- /dev/null
+++ b/src/OAuth/Exception.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+/**
+ * Generic exception class
+ */
+class Exception extends \Exception {
+       // pass
+}
diff --git a/src/OAuth/Request.php b/src/OAuth/Request.php
new file mode 100644
index 0000000..00e7a7a
--- /dev/null
+++ b/src/OAuth/Request.php
@@ -0,0 +1,337 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+/**
+ * An OAuth request
+ */
+class Request {
+       /**
+        * @var array $parameters
+        */
+       protected $parameters;
+
+       /**
+        * @var string $method
+        */
+       protected $method;
+
+       /**
+        * @var string $url
+        */
+       protected $url;
+
+       /**
+        * @var string $version
+        */
+       public static $version = '1.0';
+
+       /**
+        * Used for tests.
+        * @var string $POST_INPUT
+        */
+       public static $POST_INPUT = 'php://input';
+
+       /**
+        * @param string $method
+        * @param string $url
+        * @param array $parameters
+        */
+       function __construct( $method, $url, $parameters = null ) {
+               $parameters = $parameters ?: array();
+               $parameters = array_merge(
+                       Util::parseParameters( parse_url( $url, PHP_URL_QUERY ) 
),
+                       $parameters
+               );
+               $this->parameters = $parameters;
+               $this->method = $method;
+               $this->url = $url;
+       }
+
+
+       /**
+        * Attempt to build up a request from what was passed to the server
+        *
+        * @param string $method
+        * @param string $url
+        * @param array $params
+        * @return Request
+        */
+       public static function fromRequest(
+               $method = null,
+               $url = null,
+               array $params = null
+       ) {
+               $scheme = ( !isset( $_SERVER['HTTPS'] ) || $_SERVER['HTTPS'] != 
'on' ) ?
+                       'http' : 'https';
+               $url = ( $url ?: $scheme ) .
+                       '://' . $_SERVER['SERVER_NAME'] .
+                       ':' .
+                       $_SERVER['SERVER_PORT'] .
+                       $_SERVER['REQUEST_URI'];
+               $method = $method ?: $_SERVER['REQUEST_METHOD'];
+
+               // We weren't handed any params, so let's find the ones relevant
+               // to this request. If you run XML-RPC or similar you should 
use this
+               // to provide your own parsed parameter-list
+               if ( !$params ) {
+                       // Find request headers
+                       $headers = Util::getHeaders();
+
+                       // Parse the query-string to find GET params
+                       $params = Util::parseParameters( 
$_SERVER['QUERY_STRING'] );
+
+                       // It's a POST request of the proper content-type, so 
parse POST
+                       // params and add those overriding any duplicates from 
GET
+                       if ( $method === 'POST' &&
+                               isset( $headers['Content-Type'] ) &&
+                               strstr( $headers['Content-Type'],
+                                       'application/x-www-form-urlencoded'
+                               )
+                       ) {
+                               $post_data = Util::parseParameters(
+                                       file_get_contents( self::$POST_INPUT )
+                               );
+                               $params = array_merge( $params, $post_data );
+                       }
+
+                       // We have a Authorization-header with OAuth data. 
Parse the header
+                       // and add those overriding any duplicates from GET or 
POST
+                       if ( isset( $headers['Authorization'] ) &&
+                               substr( $headers['Authorization'], 0, 6 ) === 
'OAuth '
+                       ) {
+                               $header_params = Util::splitHeader( 
$headers['Authorization'] );
+                               $params = array_merge( $params, $header_params 
);
+                       }
+               }
+
+               return new Request( $method, $url, $params );
+       }
+
+       /**
+        * @param Consumer $consumer
+        * @param Token|null $token
+        * @param string $method
+        * @param string $url
+        * @param array $parameters
+        * @return Request
+        */
+       public static function fromConsumerAndToken(
+               Consumer $consumer,
+               /*Token*/ $token = null,
+               $method,
+               $url,
+               array $parameters = null
+       ) {
+               $parameters = $parameters ?: array();
+               $defaults = array(
+                       'oauth_version' => static::$version,
+                       'oauth_nonce' => md5( microtime() . mt_rand() ),
+                       'oauth_timestamp' => time(),
+                       'oauth_consumer_key' => $consumer->key,
+               );
+               if ( $token ) {
+                       $defaults['oauth_token'] = $token->key;
+               }
+               $parameters = array_merge( $defaults, $parameters );
+
+               return new self( $method, $url, $parameters );
+       }
+
+       public function setParameter( $name, $value, $allow_duplicates = true ) 
{
+               if ( $allow_duplicates && isset( $this->parameters[$name] ) ) {
+                       // We have already added parameter(s) with this name, 
so add to
+                       // the list
+                       if ( is_scalar( $this->parameters[$name] ) ) {
+                               // This is the first duplicate, so transform 
scalar (string)
+                               // into an array so we can add the duplicates
+                               $this->parameters[$name] = array( 
$this->parameters[$name] );
+                       }
+
+                       $this->parameters[$name][] = $value;
+               } else {
+                       $this->parameters[$name] = $value;
+               }
+       }
+
+       /**
+        * @param string $name
+        * @return mixed
+        */
+       public function getParameter( $name ) {
+               return isset( $this->parameters[$name] ) ?
+                       $this->parameters[$name] : null;
+       }
+
+       /**
+        * @return array
+        */
+       public function getParameters() {
+               return $this->parameters;
+       }
+
+       /**
+        * @param string $name
+        */
+       public function unsetParameter( $name ) {
+               unset( $this->parameters[$name] );
+       }
+
+       /**
+        * The request parameters, sorted and concatenated into a normalized 
string.
+        * @return string
+        */
+       public function getSignableParameters() {
+               // Grab all parameters
+               $params = $this->parameters;
+
+               // Remove oauth_signature if present
+               // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be 
excluded.")
+               if ( isset( $params['oauth_signature'] ) ) {
+                       unset( $params['oauth_signature'] );
+               }
+
+               return Util::buildHttpQuery( $params );
+       }
+
+       /**
+        * Returns the base string of this request
+        *
+        * The base string defined as the method, the url
+        * and the parameters (normalized), each urlencoded
+        * and the concated with &.
+        */
+       public function getSignatureBaseString() {
+               $parts = array(
+                       $this->getNormalizedMethod(),
+                       $this->getNormalizedUrl(),
+                       $this->getSignableParameters()
+               );
+
+               $parts = Util::urlencode( $parts );
+
+               return implode( '&', $parts );
+       }
+
+       /**
+        * @return string
+        */
+       public function getNormalizedMethod() {
+               return strtoupper( $this->method );
+       }
+
+       /**
+        * Parses the url and rebuilds it to be scheme://host/path
+        * @return string
+        */
+       public function getNormalizedUrl() {
+               $parts = parse_url( $this->url );
+
+               $scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'http';
+               $port = isset( $parts['port'] ) ?
+                       $parts['port'] : ( $scheme === 'https' ? '443' : '80' );
+               $host = isset( $parts['host'] ) ? strtolower( $parts['host'] ) 
: '';
+               $path = isset( $parts['path'] ) ? $parts['path'] : '';
+
+               if ( ( $scheme === 'https' && $port != '443' ) ||
+                       ( $scheme === 'http' && $port != '80' )
+               ) {
+                       $host = "{$host}:{$port}";
+               }
+               return "{$scheme}://{$host}{$path}";
+       }
+
+       /**
+        * Builds a url usable for a GET request
+        */
+       public function toUrl() {
+               $post_data = $this->toPostData();
+               $out = $this->getNormalizedUrl();
+               if ( $post_data ) {
+                       $out .= '?' . $post_data;
+               }
+               return $out;
+       }
+
+       /**
+        * Builds the data one would send in a POST request
+        */
+       public function toPostData() {
+               return Util::buildHttpQuery( $this->parameters );
+       }
+
+       /**
+        * Builds the Authorization: header
+        */
+       public function toHeader( $realm = null ) {
+               $first = true;
+               if ( $realm ) {
+                       $out = 'Authorization: OAuth realm="' .
+                               Util::urlencode( $realm ) . '"';
+                       $first = false;
+               } else {
+                       $out = 'Authorization: OAuth';
+               }
+
+               foreach ( $this->parameters as $k => $v ) {
+                       if ( substr( $k, 0, 5 ) !== 'oauth' ) {
+                               continue;
+                       }
+                       if ( is_array( $v ) ) {
+                               throw new Exception( 'Arrays not supported in 
headers' );
+                       }
+                       $out .= ( $first ) ? ' ' : ',';
+                       $out .= Util::urlencode( $k ) . '="' . Util::urlencode( 
$v ) . '"';
+                       $first = false;
+               }
+               return $out;
+       }
+
+       public function __toString() {
+               return $this->toUrl();
+       }
+
+       public function signRequest( $signature_method, $consumer, $token ) {
+               $this->setParameter(
+                       'oauth_signature_method',
+                       $signature_method->getName(),
+                       false
+               );
+               $signature = $this->buildSignature(
+                       $signature_method, $consumer, $token
+               );
+               $this->setParameter( 'oauth_signature', $signature, false );
+       }
+
+       public function buildSignature( $signature_method, $consumer, $token ) {
+               $signature = $signature_method->buildSignature(
+                       $this, $consumer, $token
+               );
+               return $signature;
+       }
+}
diff --git a/src/OAuth/SignatureMethod.php b/src/OAuth/SignatureMethod.php
new file mode 100644
index 0000000..1717f91
--- /dev/null
+++ b/src/OAuth/SignatureMethod.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+/**
+ * A class for implementing a Signature Method
+ * See section 9 ("Signing Requests") in the spec
+ */
+abstract class SignatureMethod {
+       /**
+        * Needs to return the name of the Signature Method (ie HMAC-SHA1)
+        * @return string
+        */
+       abstract public function getName();
+
+       /**
+        * 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 Request $request
+        * @param Consumer $consumer
+        * @param Token $token
+        * @return string
+        */
+       abstract public function buildSignature(
+               Request $request,
+               Consumer $consumer,
+               Token $token = null
+       );
+
+       /**
+        * Verifies that a given signature is correct
+        * @param Request $request
+        * @param Consumer $consumer
+        * @param Token|null $token
+        * @param string $signature
+        * @return bool
+        */
+       public function checkSignature(
+               Request $request,
+               Consumer $consumer,
+               /*Token*/ $token,
+               $signature
+       ) {
+               $built = $this->buildSignature( $request, $consumer, $token );
+
+               // Check for zero length, although unlikely here
+               if ( strlen( $built ) === 0 || strlen( $signature ) === 0 ) {
+                       return false;
+               }
+
+               if ( strlen( $built ) !== strlen( $signature ) ) {
+                       return false;
+               }
+
+               // Avoid a timing leak with a (hopefully) time insensitive 
compare
+               $result = 0;
+               $len = strlen( $signature );
+               for ( $i = 0; $i < $len; $i++ ) {
+                       $result |= ord( $built[$i] ) ^ ord( $signature[$i] );
+               }
+
+               return $result == 0;
+       }
+}
diff --git a/src/OAuth/SignatureMethod/HmacSha1.php 
b/src/OAuth/SignatureMethod/HmacSha1.php
new file mode 100644
index 0000000..43bc0e6
--- /dev/null
+++ b/src/OAuth/SignatureMethod/HmacSha1.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth\SignatureMethod;
+
+use Wikimedia\Slimapp\OAuth\Consumer;
+use Wikimedia\Slimapp\OAuth\Request;
+use Wikimedia\Slimapp\OAuth\SignatureMethod;
+use Wikimedia\Slimapp\OAuth\Token;
+use Wikimedia\Slimapp\OAuth\Util;
+
+/**
+ * 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 HmacSha1 extends SignatureMethod {
+       function getName() {
+               return 'HMAC-SHA1';
+       }
+
+       public function buildSignature(
+               Request $request,
+               Consumer $consumer,
+               Token $token = null
+       ) {
+               $base_string = $request->getSignatureBaseString();
+               $request->base_string = $base_string;
+
+               $key_parts = array(
+                       $consumer->secret,
+                       $token ? $token->secret : ''
+               );
+
+               $key_parts = Util::urlencode( $key_parts );
+               $key = implode( '&', $key_parts );
+
+               return base64_encode( hash_hmac( 'sha1', $base_string, $key, 
true ) );
+       }
+}
diff --git a/src/OAuth/SignatureMethod/Plaintext.php 
b/src/OAuth/SignatureMethod/Plaintext.php
new file mode 100644
index 0000000..580aa28
--- /dev/null
+++ b/src/OAuth/SignatureMethod/Plaintext.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth\SignatureMethod;
+
+use Wikimedia\Slimapp\OAuth\Consumer;
+use Wikimedia\Slimapp\OAuth\Request;
+use Wikimedia\Slimapp\OAuth\SignatureMethod;
+use Wikimedia\Slimapp\OAuth\Token;
+use Wikimedia\Slimapp\OAuth\Util;
+
+/**
+ * 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 Plaintext extends SignatureMethod {
+       public function getName() {
+               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 Request handles this!
+        */
+       public function buildSignature(
+               Request $request,
+               Consumer $consumer,
+               Token $token = null
+       ) {
+               $key_parts = array(
+                       $consumer->secret,
+                       $token ? $token->secret : ''
+               );
+
+               $key_parts = Util::urlencode( $key_parts );
+               $key = implode( '&', $key_parts );
+               $request->base_string = $key;
+
+               return $key;
+       }
+}
diff --git a/src/OAuth/SignatureMethod/RsaSha1.php 
b/src/OAuth/SignatureMethod/RsaSha1.php
new file mode 100644
index 0000000..c0cce0f
--- /dev/null
+++ b/src/OAuth/SignatureMethod/RsaSha1.php
@@ -0,0 +1,110 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+namespace Wikimedia\Slimapp\OAuth\SignatureMethod;
+
+use Wikimedia\Slimapp\OAuth\Consumer;
+use Wikimedia\Slimapp\OAuth\Request;
+use Wikimedia\Slimapp\OAuth\SignatureMethod;
+use Wikimedia\Slimapp\OAuth\Token;
+use Wikimedia\Slimapp\OAuth\Util;
+
+/**
+ * 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 RsaSha1 extends SignatureMethod {
+       public function getName() {
+               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
+       abstract protected function fetchPublicCert( Request $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
+       abstract protected function fetchPrivateCert( Request $request );
+
+       public function buildSignature(
+               Request $request,
+               Consumer $consumer,
+               Token $token = null
+       ) {
+               $base_string = $request->getSignatureBaseString();
+               $request->base_string = $base_string;
+
+               // Fetch the private key cert based on the request
+               $cert = $this->fetchPrivateCert( $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 checkSignature(
+               Request $request,
+               Consumer $consumer,
+               /*Token*/ $token,
+               $signature
+       ) {
+               $decoded_sig = base64_decode( $signature );
+
+               $base_string = $request->getSignatureBaseString();
+
+               // Fetch the public key cert based on the request
+               $cert = $this->fetchPublicCert( $request );
+
+               // Pull the public key ID from the certificate
+               $publickeyid = openssl_get_publickey( $cert );
+
+               // 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;
+       }
+}
diff --git a/src/OAuth/Token.php b/src/OAuth/Token.php
new file mode 100644
index 0000000..438955d
--- /dev/null
+++ b/src/OAuth/Token.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+/**
+ * Data type that represents an End User via either and access or requst
+ * token.
+ */
+class Token {
+       /**
+        * @var string $key
+        */
+       public $key;
+
+       /**
+        * @var string $secret
+        */
+       public $secret;
+
+       /**
+        * @param string key The token
+        * @param string secret The token secret
+        */
+       function __construct( $key, $secret ) {
+               $this->key = $key;
+               $this->secret = $secret;
+       }
+
+       /**
+        * Generate the basic string serialization of a token that a server
+        * would respond to request_token and access_token calls with
+        *
+        * @return string
+        */
+       function toString() {
+               return 'oauth_token=' . Util::urlencode( $this->key ) .
+                       '&oauth_token_secret=' . Util::urlencode( $this->secret 
);
+       }
+
+       function __toString() {
+               return $this->toString();
+       }
+}
diff --git a/src/OAuth/Util.php b/src/OAuth/Util.php
new file mode 100644
index 0000000..f8047a8
--- /dev/null
+++ b/src/OAuth/Util.php
@@ -0,0 +1,215 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+class Util {
+
+       /**
+        * @param string $input
+        * @return string
+        */
+       public static function urlencode( $input ) {
+               if ( is_array( $input ) ) {
+                       return array_map( __METHOD__, $input );
+
+               } elseif ( is_scalar( $input ) ) {
+                       return rawurlencode( $input );
+
+               } else {
+                       return '';
+               }
+       }
+
+       /**
+        * @param string $string
+        * @return string
+        */
+       public static function urldecode( $string ) {
+               return urldecode( $string );
+       }
+
+       /**
+        * Utility function for turning the Authorization: header into 
parameters,
+        * has to do some unescaping Can filter out any non-oauth parameters if
+        * needed (default behaviour)
+        *
+        * @param string $header
+        * @param bool $oauthOnly
+        * @return array
+        */
+       public static function splitHeader( $header, $oauthOnly = true ) {
+               $re = '/(' . ( $oauthOnly ? 'oauth_' : '' ) .
+                       '[a-z_-]*)=(:?"([^"]*)"|([^,]*))/';
+               $params = array();
+               if ( preg_match_all( $re, $header, $m ) ) {
+                       foreach ( $m[1] as $i => $h ) {
+                               $params[$h] = static::urldecode(
+                                       empty( $m[3][$i] ) ? $m[4][$i] : 
$m[3][$i]
+                               );
+                       }
+                       if ( isset( $params['realm'] ) ) {
+                               unset( $params['realm'] );
+                       }
+               }
+               return $params;
+       }
+
+       /**
+        * @return array
+        */
+       public static function getHeaders() {
+               if ( function_exists( 'apache_request_headers' ) ) {
+                       // we need this to get the actual Authorization: header
+                       // because apache tends to tell us it doesn't exist
+                       $headers = apache_request_headers();
+
+                       // sanitize the output of apache_request_headers because
+                       // we always want the keys to be Cased-Like-This and 
arh()
+                       // returns the headers in the same case as they are in 
the
+                       // request
+                       $out = array();
+                       foreach ( $headers as $key => $value ) {
+                               $key = str_replace(
+                                       ' ', '-',
+                                       ucwords( strtolower( str_replace( '-', 
' ', $key ) ) )
+                               );
+                               $out[$key] = $value;
+                       }
+               } else {
+                       // otherwise we don't have apache and are just going to 
have to
+                       // hope that $_SERVER actually contains what we need
+                       $out = array();
+                       if ( isset( $_SERVER['CONTENT_TYPE'] ) ) {
+                               $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
+                       }
+                       if ( isset( $_ENV['CONTENT_TYPE'] ) ) {
+                               $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
+                       }
+
+                       foreach ( $_SERVER as $key => $value ) {
+                               if ( substr( $key, 0, 5 )  == 'HTTP_' ) {
+                                       // this is chaos, basically it is just 
there to capitalize
+                                       // the first letter of every word that 
is not an initial
+                                       // HTTP and strip HTTP_ prefix
+                                       // Code from przemek
+                                       $key = str_replace(
+                                               ' ', '-',
+                                               ucwords( strtolower(
+                                                       str_replace( '_', ' ', 
substr( $key, 5 ) )
+                                               ) )
+                                       );
+                                       $out[$key] = $value;
+                               }
+                       }
+               }
+               return $out;
+       }
+
+       /**
+        * This function takes a input like a=b&a=c&d=e and returns the parsed
+        * parameters like this array('a' => array('b','c'), 'd' => 'e')
+        *
+        * @param string $input
+        * @return array
+        */
+       public static function parseParameters( $input ) {
+               if ( !isset( $input ) || !$input ) {
+                       return array();
+               }
+
+               $pairs = explode( '&', $input );
+
+               $parsed = array();
+               foreach ( $pairs as $pair ) {
+                       $split = explode( '=', $pair, 2 );
+                       $parameter = static::urldecode( $split[0] );
+                       $value = isset( $split[1] ) ? static::urldecode( 
$split[1] ) : '';
+
+                       if ( isset( $parsed[$parameter] ) ) {
+                               // We have already recieved parameter(s) with 
this name, so
+                               // add to the list of parameters with this name
+
+                               if ( is_scalar( $parsed[$parameter] ) ) {
+                                       // This is the first duplicate, so 
transform scalar
+                                       // (string) into an array so we can add 
the duplicates
+                                       $parsed[$parameter] = array( 
$parsed[$parameter] );
+                               }
+
+                               $parsed[$parameter][] = $value;
+                       } else {
+                               $parsed[$parameter] = $value;
+                       }
+               }
+               return $parsed;
+       }
+
+       /**
+        * @param array $params
+        * @return string
+        */
+       public static function buildHttpQuery( array $params ) {
+               if ( !$params ) {
+                       return '';
+               }
+
+               // Urlencode both keys and values
+               $keys = static::urlencode( array_keys( $params ) );
+               $values = static::urlencode( array_values( $params ) );
+               $params = array_combine( $keys, $values );
+
+               // Parameters are sorted by name, using lexicographical byte 
value
+               // ordering. Ref: Spec: 9.1.1 (1)
+               uksort( $params, 'strcmp' );
+
+               $pairs = array();
+               foreach ( $params as $parameter => $value ) {
+                       if ( is_array( $value ) ) {
+                               // If two or more parameters share the same 
name, they are
+                               // sorted by their value
+                               // Ref: Spec: 9.1.1 (1)
+                               sort( $value, SORT_STRING );
+                               foreach ( $value as $duplicate_value ) {
+                                       $pairs[] = 
"{$parameter}={$duplicate_value}";
+                               }
+                       } else {
+                               $pairs[] = "{$parameter}={$value}";
+                       }
+               }
+               // For each parameter, the name is separated from the 
corresponding
+               // value by an '=' character (ASCII code 61)
+               // Each name-value pair is separated by an '&' character (ASCII 
code 38)
+               return implode( '&', $pairs );
+       }
+
+       /**
+        * Disallow construction of utility class.
+        */
+       private function __construct() {
+               // no-op
+       }
+}
diff --git a/tests/OAuth/ConsumerTest.php b/tests/OAuth/ConsumerTest.php
new file mode 100644
index 0000000..8e6c0f8
--- /dev/null
+++ b/tests/OAuth/ConsumerTest.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+/**
+ * @coversDefaultClass \Wikimedia\Slimapp\OAuth\Consumer
+ */
+class ConsumerTest extends \PHPUnit_Framework_TestCase {
+       public function testConvertToString() {
+               $consumer = new Consumer( 'key', 'secret' );
+               $this->assertEquals(
+                       
'Wikimedia\Slimapp\OAuth\Consumer[key=key,secret=secret]',
+                       (string) $consumer
+               );
+       }
+}
diff --git a/tests/OAuth/RequestTest.php b/tests/OAuth/RequestTest.php
new file mode 100644
index 0000000..144ccd6
--- /dev/null
+++ b/tests/OAuth/RequestTest.php
@@ -0,0 +1,524 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+use Wikimedia\Slimapp\OAuth\SignatureMethod\HmacSha1;
+use Wikimedia\Slimapp\OAuth\SignatureMethod\Plaintext;
+
+/**
+ * @coversDefaultClass \Wikimedia\Slimapp\OAuth\Request
+ */
+class RequestTest extends \PHPUnit_Framework_TestCase {
+
+       public function testCanGetSingleParameter() {
+               $request = new Request( '', '', array( 'test'=>'foo' ) );
+               $this->assertEquals( 'foo', $request->getParameter( 'test' ),
+                       'Failed to read back parameter'
+                );
+
+               $request = new Request(
+                       '', '', array( 'test' => array( 'foo', 'bar' ) )
+                );
+               $this->assertEquals( array( 'foo', 'bar' ),
+                       $request->getParameter( 'test' ), 'Failed to read back 
parameter' );
+
+               $request = new Request(
+                       '', '', array( 'test' => 'foo', 'bar' => 'baz' )
+                );
+               $this->assertEquals( 'foo', $request->getParameter( 'test' ),
+                       'Failed to read back parameter'
+                );
+               $this->assertEquals( 'baz', $request->getParameter( 'bar' ),
+                       'Failed to read back parameter'
+                );
+       }
+
+       public function testGetAllParameters() {
+               // Yes, a awesomely boring test.. But if this doesn't work, the 
other
+               // tests is unreliable
+               $request = new Request( '', '', array( 'test' => 'foo' ) );
+               $this->assertEquals( array( 'test'=>'foo' ), 
$request->getParameters(),
+                       'Failed to read back parameters'
+                );
+
+               $request = new Request(
+                       '', '', array( 'test' => 'foo', 'bar' => 'baz' )
+                );
+               $this->assertEquals( array( 'test' => 'foo', 'bar' => 'baz' ),
+                       $request->getParameters(), 'Failed to read back 
parameters' );
+
+               $request = new Request(
+                       '', '', array( 'test' => array( 'foo', 'bar' ) )
+                );
+               $this->assertEquals( array( 'test' => array( 'foo', 'bar' ) ),
+                       $request->getParameters(), 'Failed to read back 
parameters' );
+       }
+
+       public function testSetParameters() {
+               $request = new Request( '', '' );
+               $this->assertEquals( null, $request->getParameter( 'test' ),
+                       'Failed to assert that non-existing parameter is null' 
);
+
+               $request->setParameter( 'test', 'foo' );
+               $this->assertEquals( 'foo', $request->getParameter( 'test' ),
+                       'Failed to set single-entry parameter' );
+
+               $request->setParameter( 'test', 'bar' );
+               $this->assertEquals( array( 'foo', 'bar' ),
+                       $request->getParameter( 'test' ),
+                       'Failed to set single-entry parameter'
+                );
+
+               $request->setParameter( 'test', 'bar', false );
+               $this->assertEquals( 'bar', $request->getParameter( 'test' ),
+                       'Failed to set single-entry parameter'
+                );
+       }
+
+       public function testUnsetParameter() {
+               $request = new Request( '', '' );
+               $this->assertEquals( null, $request->getParameter( 'test' ) );
+
+               $request->setParameter( 'test', 'foo' );
+               $this->assertEquals( 'foo', $request->getParameter( 'test' ) );
+
+               $request->unsetParameter( 'test' );
+               $this->assertEquals( null, $request->getParameter( 'test' ),
+                       'Failed to unset parameter'
+                );
+       }
+
+       public function testCreateRequestFromConsumerAndToken() {
+               $cons = new Consumer( 'key', 'kd94hf93k423kf44' );
+               $token = new Token( 'token', 'pfkkdhi9sl3r4s00' );
+
+               $request = Request::fromConsumerAndToken(
+                       $cons, $token, 'POST', 'http://example.com'
+                );
+               $this->assertEquals( 'POST', $request->getNormalizedMethod() );
+               $this->assertEquals( 'http://example.com', 
$request->getNormalizedUrl() );
+               $this->assertEquals( '1.0', $request->getParameter( 
'oauth_version' ) );
+               $this->assertEquals( $cons->key, $request->getParameter( 
'oauth_consumer_key' ) );
+               $this->assertEquals( $token->key, $request->getParameter( 
'oauth_token' ) );
+               $this->assertEquals( time(), $request->getParameter( 
'oauth_timestamp' ) );
+               $this->assertRegExp( '/[0-9a-f]{32}/', $request->getParameter( 
'oauth_nonce' ) );
+               // We don't know what the nonce will be, except it'll be md5 
and hence
+               // 32 hexa digits
+
+               $request = Request::fromConsumerAndToken( $cons, $token, 'POST',
+                       'http://example.com', array( 'oauth_nonce'=>'foo' ) );
+               $this->assertEquals( 'foo', $request->getParameter( 
'oauth_nonce' ) );
+
+               $request = Request::fromConsumerAndToken( $cons, null, 'POST',
+                       'http://example.com', array( 'oauth_nonce'=>'foo' ) );
+               $this->assertNull( $request->getParameter( 'oauth_token' ) );
+
+               // Test that parameters given in the $http_url instead of in the
+               // $parameters-parameter will still be picked up
+               $request = Request::fromConsumerAndToken( $cons, $token, 'POST',
+                       'http://example.com/?foo=bar' );
+               $this->assertEquals( 'http://example.com/', 
$request->getNormalizedUrl() );
+               $this->assertEquals( 'bar', $request->getParameter( 'foo' ) );
+       }
+
+       public function testBuildRequestFromPost() {
+               static::buildRequest(
+                       'POST', 'http://testbed/test', 'foo=bar&baz=blargh' );
+               $this->assertEquals( array( 'foo'=>'bar','baz'=>'blargh' ),
+                       Request::fromRequest()->getParameters(),
+                       'Failed to parse POST parameters' );
+       }
+
+       public function testBuildRequestFromGet() {
+               static::buildRequest( 'GET', 
'http://testbed/test?foo=bar&baz=blargh' );
+               $this->assertEquals( array( 'foo'=>'bar','baz'=>'blargh' ),
+                       Request::fromRequest()->getParameters(),
+                       'Failed to parse GET parameters' );
+       }
+
+       public function testBuildRequestFromHeader() {
+               $test_header = 'OAuth 
realm="",oauth_foo=bar,oauth_baz="bla,rgh"';
+               static::buildRequest( 'POST', 'http://testbed/test', '', 
$test_header );
+               $this->assertEquals(
+                       array( 'oauth_foo'=>'bar','oauth_baz'=>'bla,rgh' ),
+                       Request::fromRequest()->getParameters(),
+                       'Failed to split auth-header correctly' );
+       }
+
+       public function testHasProperParameterPriority() {
+               $test_header = 'OAuth realm="",oauth_foo=header';
+               static::buildRequest( 'POST', 
'http://testbed/test?oauth_foo=get',
+                       'oauth_foo=post', $test_header );
+               $this->assertEquals( 'header',
+                       Request::fromRequest()->getParameter( 'oauth_foo' ),
+                       'Loaded parameters in with the wrong priorities' );
+
+               static::buildRequest( 'POST', 
'http://testbed/test?oauth_foo=get',
+                       'oauth_foo=post' );
+               $this->assertEquals( 'post',
+                       Request::fromRequest()->getParameter( 'oauth_foo' ),
+                       'Loaded parameters in with the wrong priorities' );
+
+               static::buildRequest( 'POST', 
'http://testbed/test?oauth_foo=get' );
+               $this->assertEquals( 'get',
+                       Request::fromRequest()->getParameter( 'oauth_foo' ),
+                       'Loaded parameters in with the wrong priorities' );
+       }
+
+       public function testNormalizeHttpMethod() {
+               static::buildRequest( 'POST', 'http://testbed/test' );
+               $this->assertEquals( 'POST',
+                       Request::fromRequest()->getNormalizedMethod(),
+                       'Failed to normalize HTTP method: POST' );
+
+               static::buildRequest( 'post', 'http://testbed/test' );
+               $this->assertEquals( 'POST',
+                       Request::fromRequest()->getNormalizedMethod(),
+                       'Failed to normalize HTTP method: post' );
+
+               static::buildRequest( 'GET', 'http://testbed/test' );
+               $this->assertEquals( 'GET',
+                       Request::fromRequest()->getNormalizedMethod(),
+                       'Failed to normalize HTTP method: GET' );
+
+               static::buildRequest( 'PUT', 'http://testbed/test' );
+               $this->assertEquals( 'PUT',
+                       Request::fromRequest()->getNormalizedMethod(),
+                       'Failed to normalize HTTP method: PUT' );
+       }
+
+       public function testNormalizeParameters() {
+               // This is mostly repeats of OAuthUtilTest::testParseParameters
+               // & OAuthUtilTest::TestBuildHttpQuery
+
+               // Tests taken from
+               // http://wiki.oauth.net/TestCases ( "Normalize Request 
Parameters" )
+               static::buildRequest( 'POST', 'http://testbed/test', 'name' );
+               $this->assertEquals( 'name=',
+                       Request::fromRequest()->getSignableParameters() );
+
+               static::buildRequest( 'POST', 'http://testbed/test', 'a=b' );
+               $this->assertEquals( 'a=b',
+                       Request::fromRequest()->getSignableParameters() );
+
+               static::buildRequest( 'POST', 'http://testbed/test', 'a=b&c=d' 
);
+               $this->assertEquals( 'a=b&c=d',
+                       Request::fromRequest()->getSignableParameters() );
+
+               static::buildRequest( 'POST', 'http://testbed/test', 
'a=x%21y&a=x+y' );
+               $this->assertEquals( 'a=x%20y&a=x%21y',
+                       Request::fromRequest()->getSignableParameters() );
+
+               static::buildRequest( 'POST', 'http://testbed/test', 
'x%21y=a&x=a' );
+               $this->assertEquals( 'x=a&x%21y=a',
+                       Request::fromRequest()->getSignableParameters() );
+
+               static::buildRequest( 'POST',
+                       'http://testbed/test', 'a=1&c=hi 
there&f=25&f=50&f=a&z=p&z=t' );
+               $this->assertEquals( 'a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t',
+                       Request::fromRequest()->getSignableParameters() );
+       }
+
+       public function testNormalizeHttpUrl() {
+               static::buildRequest( 'POST', 'http://example.com' );
+               $this->assertEquals( 'http://example.com',
+                       Request::fromRequest()->getNormalizedUrl() );
+
+               static::buildRequest( 'POST', 'https://example.com' );
+               $this->assertEquals( 'https://example.com',
+                       Request::fromRequest()->getNormalizedUrl() );
+
+               // Tests that http on !80 and https on !443 keeps the port
+               static::buildRequest( 'POST', 'http://example.com:8080' );
+               $this->assertEquals( 'http://example.com:8080',
+                       Request::fromRequest()->getNormalizedUrl() );
+
+               static::buildRequest( 'POST', 'https://example.com:80' );
+               $this->assertEquals( 'https://example.com:80',
+                       Request::fromRequest()->getNormalizedUrl() );
+
+               static::buildRequest( 'POST', 'http://example.com:443' );
+               $this->assertEquals( 'http://example.com:443',
+                       Request::fromRequest()->getNormalizedUrl() );
+
+               static::buildRequest( 'POST', 'http://Example.COM' );
+               $this->assertEquals( 'http://example.com',
+                       Request::fromRequest()->getNormalizedUrl() );
+
+               // Emulate silly behavior by some clients, where there Host 
header
+               // includes the port
+               static::buildRequest( 'POST', 'http://example.com' );
+               $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] . ':' .
+                       $_SERVER['SERVER_PORT'];
+               $this->assertEquals( 'http://example.com',
+                       Request::fromRequest()->getNormalizedUrl() );
+       }
+
+       public function testBuildPostData() {
+               static::buildRequest( 'POST', 'http://example.com' );
+               $this->assertEquals( '', Request::fromRequest()->toPostData() );
+
+               static::buildRequest( 'POST', 'http://example.com', 'foo=bar' );
+               $this->assertEquals( 'foo=bar', 
Request::fromRequest()->toPostData() );
+
+               static::buildRequest( 'GET', 'http://example.com?foo=bar' );
+               $this->assertEquals( 'foo=bar', 
Request::fromRequest()->toPostData() );
+       }
+
+       public function testBuildUrl() {
+               static::buildRequest( 'POST', 'http://example.com' );
+               $this->assertEquals( 'http://example.com',
+                       Request::fromRequest()->toUrl() );
+
+               static::buildRequest( 'POST', 'http://example.com', 'foo=bar' );
+               $this->assertEquals( 'http://example.com?foo=bar',
+                       Request::fromRequest()->toUrl() );
+
+               static::buildRequest( 'GET', 'http://example.com?foo=bar' );
+               $this->assertEquals( 'http://example.com?foo=bar',
+                       Request::fromRequest()->toUrl() );
+       }
+
+       public function testConvertToString() {
+               static::buildRequest( 'POST', 'http://example.com' );
+               $this->assertEquals( 'http://example.com',
+                       (string)Request::fromRequest() );
+
+               static::buildRequest( 'POST', 'http://example.com', 'foo=bar' );
+               $this->assertEquals( 'http://example.com?foo=bar',
+                       (string)Request::fromRequest() );
+
+               static::buildRequest( 'GET', 'http://example.com?foo=bar' );
+               $this->assertEquals( 'http://example.com?foo=bar',
+                       (string)Request::fromRequest() );
+       }
+
+       public function testBuildHeader() {
+               static::buildRequest( 'POST', 'http://example.com' );
+               $this->assertEquals( 'Authorization: OAuth',
+                       Request::fromRequest()->toHeader() );
+               $this->assertEquals( 'Authorization: OAuth realm="test"',
+                       Request::fromRequest()->toHeader( 'test' ) );
+
+               static::buildRequest( 'POST', 'http://example.com', 'foo=bar' );
+               $this->assertEquals( 'Authorization: OAuth',
+                       Request::fromRequest()->toHeader() );
+               $this->assertEquals( 'Authorization: OAuth realm="test"',
+                       Request::fromRequest()->toHeader( 'test' ) );
+
+               static::buildRequest( 'POST', 'http://example.com', 
'oauth_test=foo' );
+               $this->assertEquals( 'Authorization: OAuth oauth_test="foo"',
+                       Request::fromRequest()->toHeader() );
+               $this->assertEquals(
+                       'Authorization: OAuth realm="test",oauth_test="foo"',
+                       Request::fromRequest()->toHeader( 'test' ) );
+
+               // Is headers supposted to be Urlencoded. More to the point:
+               // Should it be baz = bla,rgh or baz = bla%2Crgh ??
+               // - morten.fangel
+               static::buildRequest( 'POST', 'http://example.com',
+                       '', 'OAuth realm="",oauth_foo=bar,oauth_baz="bla,rgh"' 
);
+               $this->assertEquals(
+                       'Authorization: OAuth 
oauth_foo="bar",oauth_baz="bla%2Crgh"',
+                       Request::fromRequest()->toHeader() );
+               $this->assertEquals(
+                       'Authorization: OAuth 
realm="test",oauth_foo="bar",oauth_baz="bla%2Crgh"',
+                       Request::fromRequest()->toHeader( 'test' ) );
+       }
+
+       public function testWontBuildHeaderWithArrayInput() {
+               $this->setExpectedException( 
'Wikimedia\Slimapp\OAuth\Exception' );
+               static::buildRequest( 'POST', 'http://example.com',
+                       'oauth_foo=bar&oauth_foo=baz' );
+               Request::fromRequest()->toHeader();
+       }
+
+       public function testBuildBaseString() {
+               static::buildRequest( 'POST', 'http://testbed/test', 'n=v' );
+               $this->assertEquals(
+                       'POST&http%3A%2F%2Ftestbed%2Ftest&n%3Dv',
+                       Request::fromRequest()->getSignatureBaseString()
+                );
+
+               static::buildRequest( 'POST', 'http://testbed/test', 'n=v&n=v2' 
);
+               $this->assertEquals( 
'POST&http%3A%2F%2Ftestbed%2Ftest&n%3Dv%26n%3Dv2',
+                       Request::fromRequest()->getSignatureBaseString() );
+
+               static::buildRequest( 'GET', 'http://example.com?n=v' );
+               $this->assertEquals( 'GET&http%3A%2F%2Fexample.com&n%3Dv',
+                       Request::fromRequest()->getSignatureBaseString() );
+
+               $params = 
'oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03'
+                       . '&oauth_timestamp=1191242090'
+                       . '&oauth_nonce=hsu94j3884jdopsl'
+                       . 
'&oauth_signature_method=PLAINTEXT&oauth_signature=ignored';
+               static::buildRequest( 'POST', 
'https://photos.example.net/request_token', $params );
+               $this->assertEquals(
+                       
'POST&https%3A%2F%2Fphotos.example.net%2Frequest_token&oauth_'
+                       . 
'consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dhsu94j3884j'
+                       . 
'dopsl%26oauth_signature_method%3DPLAINTEXT%26oauth_timestam'
+                       . 'p%3D1191242090%26oauth_version%3D1.0',
+                       Request::fromRequest()->getSignatureBaseString() );
+
+               $params = 'file=vacation.jpg&size=original&oauth_version=1.0';
+               $params .= '&oauth_consumer_key=dpf43f3p2l4k3l03';
+               $params .= 
'&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096';
+               $params .= '&oauth_nonce=kllo9940pd9333jh';
+               $params .= 
'&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1';
+               static::buildRequest( 'GET', 
'http://photos.example.net/photos?'.$params );
+               $this->assertEquals(
+                       
'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation'
+                       . 
'.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%'
+                       . 
'3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26o'
+                       . 
'auth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jd'
+                       . 'k%26oauth_version%3D1.0%26size%3Doriginal',
+                       Request::fromRequest()->getSignatureBaseString() );
+       }
+
+       public function testBuildSignature() {
+               $params = 'file=vacation.jpg&size=original&oauth_version=1.0'
+                       . '&oauth_consumer_key=dpf43f3p2l4k3l03'
+                       . '&oauth_token=nnch734d00sl2jdk'
+                       . 
'&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh'
+                       . 
'&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1';
+               static::buildRequest( 'GET', 
'http://photos.example.net/photos?'.$params );
+               $r = Request::fromRequest();
+
+               $cons = new Consumer( 'key', 'kd94hf93k423kf44' );
+               $token = new Token( 'token', 'pfkkdhi9sl3r4s00' );
+
+               $hmac = new HmacSha1();
+               $plaintext = new Plaintext();
+
+               $this->assertEquals( 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=',
+                       $r->buildSignature( $hmac, $cons, $token ) );
+
+               $this->assertEquals( 'kd94hf93k423kf44&pfkkdhi9sl3r4s00',
+                       $r->buildSignature( $plaintext, $cons, $token ) );
+       }
+
+       public function testSign() {
+               $params = 'file=vacation.jpg&size=original&oauth_version=1.0'
+                       . '&oauth_consumer_key=dpf43f3p2l4k3l03'
+                       . '&oauth_token=nnch734d00sl2jdk'
+                       . 
'&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh'
+                       . 
'&oauth_signature=__ignored__&oauth_signature_method=HMAC-SHA1';
+               static::buildRequest( 'GET',
+                       'http://photos.example.net/photos?'.$params );
+               $r = Request::fromRequest();
+
+               $cons = new Consumer( 'key', 'kd94hf93k423kf44' );
+               $token = new Token( 'token', 'pfkkdhi9sl3r4s00' );
+
+               $hmac = new HmacSha1();
+               $plaintext = new Plaintext();
+
+               // We need to test both what the parameter is, and how the 
serialized
+               // request is..
+
+               $r->signRequest( $hmac, $cons, $token );
+               $this->assertEquals( 'HMAC-SHA1',
+                       $r->getParameter( 'oauth_signature_method' ) );
+               $this->assertEquals( 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=',
+                       $r->getParameter( 'oauth_signature' ) );
+               $expectedPostdata = 'file=vacation.jpg'
+                       . '&oauth_consumer_key=dpf43f3p2l4k3l03'
+                       . '&oauth_nonce=kllo9940pd9333jh'
+                       . 
'&oauth_signature=tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D'
+                       . '&oauth_signature_method=HMAC-SHA1'
+                       . 
'&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk'
+                       . '&oauth_version=1.0&size=original';
+               $this->assertEquals( $expectedPostdata, $r->toPostData() );
+
+               $r->signRequest( $plaintext, $cons, $token );
+               $this->assertEquals( 'PLAINTEXT',
+                       $r->getParameter( 'oauth_signature_method' ) );
+               $this->assertEquals( 'kd94hf93k423kf44&pfkkdhi9sl3r4s00',
+                       $r->getParameter( 'oauth_signature' ) );
+               $expectedPostdata = 'file=vacation.jpg'
+                       . '&oauth_consumer_key=dpf43f3p2l4k3l03'
+                       . '&oauth_nonce=kllo9940pd9333jh&'
+                       . 'oauth_signature=kd94hf93k423kf44%26pfkkdhi9sl3r4s00'
+                       . '&oauth_signature_method=PLAINTEXT'
+                       . 
'&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk'
+                       . '&oauth_version=1.0&size=original';
+               $this->assertEquals( $expectedPostdata, $r->toPostData() );
+
+       }
+
+       /**
+        * Populates $_{SERVER,GET,POST} and whatever environment-variables 
needed
+        * to test everything.
+        *
+        * @param string $method GET or POST
+        * @param string $uri What URI is the request to ( eg 
http://example.com/foo?bar=baz )
+        * @param string $post_data What should the post-data be
+        * @param string $auth_header What to set the Authorization header to
+        */
+       public static function buildRequest(
+               $method, $uri, $post_data = '', $auth_header = ''
+        ) {
+               $_SERVER = array();
+               $_POST = array();
+               $_GET = array();
+
+               $method = strtoupper( $method );
+
+               $parts = parse_url( $uri );
+
+               $scheme = $parts['scheme'];
+               $port   = isset( $parts['port'] ) && $parts['port'] ?
+                       $parts['port'] : ( $scheme === 'https' ? '443' : '80' );
+               $host   = $parts['host'];
+               $path   = isset( $parts['path'] )  ? $parts['path']  : null;
+               $query  = isset( $parts['query'] ) ? $parts['query'] : null;
+
+               if ( $scheme == 'https' ) {
+                       $_SERVER['HTTPS'] = 'on';
+               }
+
+               $_SERVER['REQUEST_METHOD'] = $method;
+               $_SERVER['HTTP_HOST'] = $host;
+               $_SERVER['SERVER_NAME'] = $host;
+               $_SERVER['SERVER_PORT'] = $port;
+               $_SERVER['SCRIPT_NAME'] = $path;
+               $_SERVER['REQUEST_URI'] = $path . '?' . $query;
+               $_SERVER['QUERY_STRING'] = $query . '';
+               parse_str( $query, $_GET );
+
+               if ( $method == 'POST' ) {
+                       $_SERVER['HTTP_CONTENT_TYPE'] = 
'application/x-www-form-urlencoded';
+                       $_POST = parse_str( $post_data );
+                       Request::$POST_INPUT = 
'data:application/x-www-form-urlencoded,'.$post_data;
+               }
+
+               if ( $auth_header != '' ) {
+                       $_SERVER['HTTP_AUTHORIZATION'] = $auth_header;
+               }
+       }
+}
diff --git a/tests/OAuth/SignatureMethod/HmacSha1Test.php 
b/tests/OAuth/SignatureMethod/HmacSha1Test.php
new file mode 100644
index 0000000..a9e9f22
--- /dev/null
+++ b/tests/OAuth/SignatureMethod/HmacSha1Test.php
@@ -0,0 +1,116 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth\SignatureMethod;
+
+use Wikimedia\Slimapp\OAuth\Consumer;
+use Wikimedia\Slimapp\OAuth\Request;
+use Wikimedia\Slimapp\OAuth\Token;
+
+/**
+ * @coversDefaultClass \Wikimedia\Slimapp\OAuth\SignatureMethod\HmacSha1
+ */
+class HmacSha1Test extends \PHPUnit_Framework_TestCase {
+       private $method;
+
+       public function setUp() {
+               $this->method = new HmacSha1();
+       }
+
+       public function testIdentifyAsHmacSha1() {
+               $this->assertEquals( 'HMAC-SHA1', $this->method->getName() );
+       }
+
+       public function testBuildSignature() {
+               // Tests taken from http://wiki.oauth.net/TestCases section 9.2 
( "HMAC-SHA1" )
+               $request = $this->mockRequest( 'bs' );
+               $consumer = new Consumer( '__unused__', 'cs' );
+               $token = null;
+               $this->assertEquals( 'egQqG5AJep5sJ7anhXju1unge2I=',
+                       $this->method->buildSignature( $request, $consumer, 
$token ) );
+
+               $request  = $this->mockRequest( 'bs' );
+               $consumer = new Consumer( '__unused__', 'cs' );
+               $token    = new Token( '__unused__', 'ts' );
+               $this->assertEquals( 'VZVjXceV7JgPq/dOTnNmEfO0Fv8=',
+                       $this->method->buildSignature( $request, $consumer, 
$token ) );
+
+               $request  = $this->mockRequest(
+                       
'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26'
+                       . 'oauth_consumer_key%3Ddpf43f3p2l4k3l03%26'
+                       . 'oauth_nonce%3Dkllo9940pd9333jh%26'
+                       . 'oauth_signature_method%3DHMAC-SHA1%26'
+                       . 'oauth_timestamp%3D1191242096%26'
+                       . 'oauth_token%3Dnnch734d00sl2jdk%26'
+                       . 'oauth_version%3D1.0%26size%3Doriginal' );
+               $consumer = new Consumer( '__unused__', 'kd94hf93k423kf44' );
+               $token    = new Token( '__unused__', 'pfkkdhi9sl3r4s00' );
+               $this->assertEquals( 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=',
+                       $this->method->buildSignature( $request, $consumer, 
$token ) );
+       }
+
+       public function testVerifySignature() {
+               // Tests taken from http://wiki.oauth.net/TestCases section 9.2
+               // ( "HMAC-SHA1" )
+               $request   = $this->mockRequest( 'bs' );
+               $consumer  = new Consumer( '__unused__', 'cs' );
+               $token     = null;
+               $signature = 'egQqG5AJep5sJ7anhXju1unge2I=';
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature ) );
+
+               $request   = $this->mockRequest( 'bs' );
+               $consumer  = new Consumer( '__unused__', 'cs' );
+               $token     = new Token( '__unused__', 'ts' );
+               $signature = 'VZVjXceV7JgPq/dOTnNmEfO0Fv8=';
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature ) );
+
+               $request   = $this->mockRequest(
+                       
'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26'
+                       . 'oauth_consumer_key%3Ddpf43f3p2l4k3l03%26'
+                       . 'oauth_nonce%3Dkllo9940pd9333jh%26'
+                       . 'oauth_signature_method%3DHMAC-SHA1%26'
+                       . 'oauth_timestamp%3D1191242096%26'
+                       . 'oauth_token%3Dnnch734d00sl2jdk%26'
+                       . 'oauth_version%3D1.0%26size%3Doriginal' );
+               $consumer  = new Consumer( '__unused__', 'kd94hf93k423kf44' );
+               $token     = new Token( '__unused__', 'pfkkdhi9sl3r4s00' );
+               $signature = 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=';
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature ) );
+       }
+
+       protected function mockRequest( $baseStr ) {
+               $stub = $this->getMockBuilder( 
'Wikimedia\Slimapp\OAuth\request' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $stub->method( 'getSignatureBaseString' )
+                       ->willReturn( $baseStr );
+               return $stub;
+       }
+}
diff --git a/tests/OAuth/SignatureMethod/PlaintextTest.php 
b/tests/OAuth/SignatureMethod/PlaintextTest.php
new file mode 100644
index 0000000..137b6a7
--- /dev/null
+++ b/tests/OAuth/SignatureMethod/PlaintextTest.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth\SignatureMethod;
+
+use Wikimedia\Slimapp\OAuth\Consumer;
+use Wikimedia\Slimapp\OAuth\Request;
+use Wikimedia\Slimapp\OAuth\Token;
+
+/**
+ * @coversDefaultClass \Wikimedia\Slimapp\OAuth\SignatureMethod\Plaintext
+ */
+class PlaintextTest extends \PHPUnit_Framework_TestCase {
+       private $method;
+
+       public function setUp() {
+               $this->method = new Plaintext();
+       }
+
+       public function testIdentifyAsPlaintext() {
+               $this->assertEquals( 'PLAINTEXT', $this->method->getName() );
+       }
+
+       public function testBuildSignature() {
+               // Tests based on from http://wiki.oauth.net/TestCases section 
9.2 ( "HMAC-SHA1" )
+               $request  = $this->mockRequest( '__unused__' );
+               $consumer = new Consumer( '__unused__', 'cs' );
+               $token = null;
+               $this->assertEquals( 'cs&', $this->method->buildSignature( 
$request, $consumer, $token ) );
+
+               $request  = $this->mockRequest( '__unused__' );
+               $consumer = new Consumer( '__unused__', 'cs' );
+               $token    = new Token( '__unused__', 'ts' );
+               $this->assertEquals( 'cs&ts', $this->method->buildSignature( 
$request, $consumer, $token ) );
+
+               $request  = $this->mockRequest( '__unused__' );
+               $consumer = new Consumer( '__unused__', 'kd94hf93k423kf44' );
+               $token    = new Token( '__unused__', 'pfkkdhi9sl3r4s00' );
+               $this->assertEquals( 'kd94hf93k423kf44&pfkkdhi9sl3r4s00',
+                       $this->method->buildSignature( $request, $consumer, 
$token ) );
+
+               // Tests taken from Chapter 9.4.1 ( "Generating Signature" ) 
from the spec
+               $request  = $this->mockRequest( '__unused__' );
+               $consumer = new Consumer( '__unused__', 'djr9rjt0jd78jf88' );
+               $token    = new Token( '__unused__', 'jjd999tj88uiths3' );
+               $this->assertEquals( 'djr9rjt0jd78jf88&jjd999tj88uiths3',
+                       $this->method->buildSignature( $request, $consumer, 
$token ) );
+
+               $request  = $this->mockRequest( '__unused__' );
+               $consumer = new Consumer( '__unused__', 'djr9rjt0jd78jf88' );
+               $token    = new Token( '__unused__', 'jjd99$tj88uiths3' );
+               $this->assertEquals( 'djr9rjt0jd78jf88&jjd99%24tj88uiths3',
+                       $this->method->buildSignature( $request, $consumer, 
$token ) );
+       }
+
+       public function testVerifySignature() {
+               // Tests based on from http://wiki.oauth.net/TestCases section 
9.2 ( "HMAC-SHA1" )
+               $request = $this->mockRequest( '__unused__' );
+               $consumer = new Consumer( '__unused__', 'cs' );
+               $token = null;
+               $signature = 'cs&';
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature ) );
+
+               $request   = $this->mockRequest( '__unused__' );
+               $consumer  = new Consumer( '__unused__', 'cs' );
+               $token     = new Token( '__unused__', 'ts' );
+               $signature = 'cs&ts';
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature ) );
+
+               $request   = $this->mockRequest( '__unused__' );
+               $consumer  = new Consumer( '__unused__', 'kd94hf93k423kf44' );
+               $token     = new Token( '__unused__', 'pfkkdhi9sl3r4s00' );
+               $signature = 'kd94hf93k423kf44&pfkkdhi9sl3r4s00';
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature ) );
+
+               // Tests taken from Chapter 9.4.1 ( "Generating Signature" ) 
from the
+               // spec
+               $request   = $this->mockRequest( '__unused__' );
+               $consumer  = new Consumer( '__unused__', 'djr9rjt0jd78jf88' );
+               $token     = new Token( '__unused__', 'jjd999tj88uiths3' );
+               $signature = 'djr9rjt0jd78jf88&jjd999tj88uiths3';
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature ) );
+
+               $request   = $this->mockRequest( '__unused__' );
+               $consumer  = new Consumer( '__unused__', 'djr9rjt0jd78jf88' );
+               $token     = new Token( '__unused__', 'jjd99$tj88uiths3' );
+               $signature = 'djr9rjt0jd78jf88&jjd99%24tj88uiths3';
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature ) );
+       }
+
+       protected function mockRequest( $baseStr ) {
+               $stub = $this->getMockBuilder( 
'Wikimedia\Slimapp\OAuth\request' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $stub->method( 'getSignatureBaseString' )
+                       ->willReturn( $baseStr );
+               return $stub;
+       }
+}
diff --git a/tests/OAuth/SignatureMethod/RsaSha1Test.php 
b/tests/OAuth/SignatureMethod/RsaSha1Test.php
new file mode 100644
index 0000000..c1da1ba
--- /dev/null
+++ b/tests/OAuth/SignatureMethod/RsaSha1Test.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth\SignatureMethod;
+
+use Wikimedia\Slimapp\OAuth\Consumer;
+use Wikimedia\Slimapp\OAuth\Request;
+use Wikimedia\Slimapp\OAuth\Token;
+
+/**
+ * @coversDefaultClass \Wikimedia\Slimapp\OAuth\SignatureMethod\RsaSha1
+ */
+class RsaSha1Test extends \PHPUnit_Framework_TestCase {
+       private $method;
+
+       public function setUp() {
+               $cert = <<<EOD
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V
+A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d
+7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ
+hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H
+X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm
+uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw
+rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z
+zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn
+qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG
+WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno
+cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+
+3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8
+AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54
+Lw03eHTNQghS0A==
+-----END PRIVATE KEY-----
+EOD;
+               $pubcert = <<<EOD
+-----BEGIN CERTIFICATE-----
+MIIBpjCCAQ+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAZMRcwFQYDVQQDDA5UZXN0
+IFByaW5jaXBhbDAeFw03MDAxMDEwODAwMDBaFw0zODEyMzEwODAwMDBaMBkxFzAV
+BgNVBAMMDlRlc3QgUHJpbmNpcGFsMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQC0YjCwIfYoprq/FQO6lb3asXrxLlJFuCvtinTF5p0GxvQGu5O3gYytUvtC2JlY
+zypSRjVxwxrsuRcP3e641SdASwfrmzyvIgP08N4S0IFzEURkV1wp/IpH7kH41Etb
+mUmrXSwfNZsnQRE5SYSOhh+LcK2wyQkdgcMv11l4KoBkcwIDAQABMA0GCSqGSIb3
+DQEBBQUAA4GBAGZLPEuJ5SiJ2ryq+CmEGOXfvlTtEL2nuGtr9PewxkgnOjZpUy+d
+4TvuXJbNQc8f4AMWL/tO9w0Fk80rWKp9ea8/df4qMq5qlFWlx6yOLQxumNOmECKb
+WpkUQDIDJEoFUzKMVuJf4KO/FJ345+BNLGgbJ6WujreoM1X/gYfdnJ/J
+-----END CERTIFICATE-----
+EOD;
+
+               $this->method = $this->getMockBuilder(
+                       'Wikimedia\Slimapp\OAuth\SignatureMethod\RsaSha1' )
+                       ->setMethods( array( 'fetchPrivateCert', 
'fetchPublicCert' ) )
+                       ->getMock();
+
+               $this->method->method( 'fetchPrivateCert' )
+                       ->willReturn( $cert );
+               $this->method->method( 'fetchPublicCert' )
+                       ->willReturn( $pubcert );
+       }
+
+       public function testIdentifyAsRsaSha1() {
+               $this->assertEquals( 'RSA-SHA1', $this->method->getName() );
+       }
+
+       public function testBuildSignature() {
+               if ( !function_exists( 'openssl_get_privatekey' ) ) {
+                       $this->markTestSkipped(
+                               'OpenSSL not available, can\'t test RSA-SHA1 
functionality'
+                       );
+               }
+
+               // Tests taken from http://wiki.oauth.net/TestCases section 9.3
+               // ("RSA-SHA1")
+               $request = $this->mockRequest(
+                       // @codingStandardsIgnoreStart Line exceeds 100 
characters
+                       
'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal'
+                       // @codingStandardsIgnoreEnd
+               );
+               $consumer = new Consumer( 'dpf43f3p2l4k3l03', '__unused__' );
+               $token = null;
+               // @codingStandardsIgnoreStart Line exceeds 100 characters
+               $signature = 
'jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=';
+               // @codingStandardsIgnoreEnd
+               $this->assertEquals( $signature,
+                       $this->method->buildSignature( $request, $consumer, 
$token )
+               );
+       }
+
+       public function testVerifySignature() {
+               if ( !function_exists( 'openssl_get_privatekey' ) ) {
+                       $this->markTestSkipped(
+                               'OpenSSL not available, can\'t test RSA-SHA1 
functionality'
+                       );
+               }
+
+               // Tests taken from http://wiki.oauth.net/TestCases section 9.3
+               // ("RSA-SHA1")
+               $request = $this->mockRequest(
+                       // @codingStandardsIgnoreStart Line exceeds 100 
characters
+                       
'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal'
+                       // @codingStandardsIgnoreEnd
+               );
+               $consumer = new Consumer( 'dpf43f3p2l4k3l03', '__unused__' );
+               $token = null;
+               // @codingStandardsIgnoreStart Line exceeds 100 characters
+               $signature = 
'jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=';
+               // @codingStandardsIgnoreEnd
+               $this->assertTrue( $this->method->checkSignature(
+                       $request, $consumer, $token, $signature
+               ) );
+       }
+
+       protected function mockRequest( $baseStr ) {
+               $stub = $this->getMockBuilder( 
'Wikimedia\Slimapp\OAuth\request' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $stub->method( 'getSignatureBaseString' )
+                       ->willReturn( $baseStr );
+               return $stub;
+       }
+}
diff --git a/tests/OAuth/TokenTest.php b/tests/OAuth/TokenTest.php
new file mode 100644
index 0000000..eecde55
--- /dev/null
+++ b/tests/OAuth/TokenTest.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @section LICENSE
+ * Copyright (c) 2007 Andy Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace Wikimedia\Slimapp\OAuth;
+
+/**
+ * @coversDefaultClass \Wikimedia\Slimapp\OAuth\Token
+ */
+class TokenTest extends \PHPUnit_Framework_TestCase {
+       public function testSerialize() {
+               $token = new Token( 'token', 'secret' );
+               $this->assertEquals(
+                       'oauth_token=token&oauth_token_secret=secret', 
$token->toString()
+               );
+
+               $token = new Token( 'token&', 'secret%' );
+               $this->assertEquals(
+                       'oauth_token=token%26&oauth_token_secret=secret%25',
+                       $token->toString()
+               );
+       }
+       public function testConvertToString() {
+               $token = new Token( 'token', 'secret' );
+               $this->assertEquals(
+                       'oauth_token=token&oauth_token_secret=secret', 
(string)$token
+               );
+
+               $token = new Token( 'token&', 'secret%' );
+               $this->assertEquals(
+                       'oauth_token=token%26&oauth_token_secret=secret%25',
+                       (string)$token
+               );
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/234917
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Id8222bbe9960942f94fd67d7fbc3290cb0105245
Gerrit-PatchSet: 6
Gerrit-Project: wikimedia/slimapp
Gerrit-Branch: master
Gerrit-Owner: BryanDavis <bda...@wikimedia.org>
Gerrit-Reviewer: BryanDavis <bda...@wikimedia.org>
Gerrit-Reviewer: CSteipp <cste...@wikimedia.org>
Gerrit-Reviewer: Niharika29 <niharikakohl...@gmail.com>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to