CSteipp has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/70316


Change subject: Basic MediaWiki integration, library cleanup
......................................................................

Basic MediaWiki integration, library cleanup

WIP, but /initial is mostly working

Change-Id: Ib6dd320b58f7ba2e4df3a8545cf9f3a790c2fa9b
---
M OAuth.config.php
M OAuth.setup.php
A backend/MWOAuthConsumer.php
A backend/MWOAuthDataStore.php
A backend/MWOAuthException.php
A backend/MWOAuthRequest.php
A backend/MWOAuthSignatureMethod.php
A backend/MWOAuthToken.php
A backend/MWOAuthUtils.php
M frontend/OAuthUI.setup.php
A frontend/specials/SpecialOAuth.php
M lib/OAuth.php
12 files changed, 704 insertions(+), 34 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/OAuth 
refs/changes/16/70316/1

diff --git a/OAuth.config.php b/OAuth.config.php
index fb7d4d5..e10b3ad 100644
--- a/OAuth.config.php
+++ b/OAuth.config.php
@@ -6,3 +6,11 @@
 
 # End of configuration variables.
 # ########
+
+$wgOAuthStorage = array(
+       'data' => 'OAuthStoreMysql',
+       'cache' => CACHE_MEMCACHED,
+       'log' => 'ExternalStoreDB',
+);
+
+$wgOAuthStorageDB = '';
diff --git a/OAuth.setup.php b/OAuth.setup.php
index 52d15ac..73f4dd8 100644
--- a/OAuth.setup.php
+++ b/OAuth.setup.php
@@ -22,6 +22,7 @@
                $frontendDir      = "$dir/frontend";
                $langDir          = "$dir/frontend/language/";
                $spActionDir      = "$dir/frontend/specialpages/actions";
+               $libDir           = "$dir/lib";
 
                # Main i18n file and special page alias file
                $messagesFiles['OAuth'] = "$langDir/OAuth.i18n.php";
@@ -40,11 +41,32 @@
                # Special:OAuth/token?
 
                # Utility functions
-               $classes['OAuth'] = "$backendDir/OAuthUtils.php";
+               $classes['MWOAuthUtils'] = "$backendDir/MWOAuthUtils.php";
+               $classes['MWOAuthException'] = 
"$backendDir/MWOAuthException.php";
 
                # Data access objects
+               $classes['MWOAuthToken'] = "$backendDir/MWOAuthToken.php";
+               $classes['MWOAuthConsumer'] = "$backendDir/MWOAuthConsumer.php";
 
                # Business logic
+               $classes['MWOAuthRequest'] = "$backendDir/MWOAuthRequest.php";
+               $classes['MWOAuthSignatureMethod_RSA_SHA1'] = 
"$backendDir/MWOAuthSignatureMethod.php";
+               $classes['SpecialOAuth'] = 
"$frontendDir/specials/SpecialOAuth.php";
+
+               # Library
+               $classes['OAuthException'] = "$libDir/OAuth.php";
+               $classes['OAuthConsumer'] = "$libDir/OAuth.php";
+               $classes['OAuthToken'] = "$libDir/OAuth.php";
+               $classes['OAuthSignatureMethod'] = "$libDir/OAuth.php";
+               $classes['OAuthSignatureMethod_HMAC_SHA1'] = 
"$libDir/OAuth.php";
+               $classes['OAuthSignatureMethod_RSA_SHA1'] = "$libDir/OAuth.php";
+               $classes['OAuthRequest'] = "$libDir/OAuth.php";
+               $classes['OAuthServer'] = "$libDir/OAuth.php";
+               $classes['OAuthDataStore'] = "$libDir/OAuth.php";
+               $classes['OAuthUtil'] = "$libDir/OAuth.php";
+
+               # Storage
+               $classes['MWOAuthDataStore'] = 
"$backendDir/MWOAuthDataStore.php";
 
                # Schema changes
                $classes['OAuthUpdaterHooks'] = 
"$schemaDir/OAuthUpdater.hooks.php";
diff --git a/backend/MWOAuthConsumer.php b/backend/MWOAuthConsumer.php
new file mode 100644
index 0000000..7967734
--- /dev/null
+++ b/backend/MWOAuthConsumer.php
@@ -0,0 +1,80 @@
+<?php
+
+class MWOAuthConsumer extends OAuthConsumer {
+
+       // parent class (OAuthConsumer) tracks: key, secret, callback_url
+       // Database id. Not sure if we need to track it here?
+       public $id;
+
+       // Consumer Owner, their global user_id (CentralAuth id, if CentralAuth 
is used)
+       public $user_id;
+       // The App Owner's email address
+       public $email;
+
+       // App Name (public)
+       public $name;
+       // App version
+       public $version;
+       // App description for the authorizing user
+       public $description;
+       // Date the App was registered
+       public $registration;
+       // Stage
+       public $registration_stage;
+       // Has this App been deleted
+       public $deleted;
+
+       // IP restriction on App calls, if any
+       public $origin_restrictions;
+       // The wiki where this version of this app has requested permissions, 
or * for any
+       public $wiki;
+       // Public cert of the rsa key owned by the Consumer
+       public $rsa_cert;
+
+       public function __construct( $id, $key, $secret, $callback, $id, 
$user_id, $email, $name, $version, $description, $regDate, $ipRestrictions, 
$wiki, $rsaKey, $regStage, $deleted ) {
+               $this->id = $id;
+               $this->key = $key;
+               $this->secret = $secret;
+               $this->callback_url = $callback;
+               $this->oarc_id = $id;
+               $this->user_id = $user_id;
+               $this->email =  $email;
+               $this->name = $name;
+               $this->version = $version;
+               $this->description = $description;
+               $this->registration = $regDate;
+               $this->origin_restrictions = $ipRestrictions;
+               $this->wiki = $wiki;
+               $this->rsa_cert = $rsaKey;
+               $this->registration_stage = $regStage;
+               $this->deleted = $deleted;
+       }
+
+       /**
+        *
+        *
+        *
+        */
+       public static function newFromRow( $row ) {
+               return new self(
+                       $row->oarc_id,
+                       $row->oarc_consumer_key,
+                       $row->oarc_secret_key,
+                       $row->oarc_callback_url,
+                       $row->oarc_id,
+                       $row->oarc_user_id,
+                       $row->oarc_email,
+                       $row->oarc_name,
+                       $row->oarc_version,
+                       $row->oarc_description,
+                       $row->oarc_registration,
+                       $row->oarc_origin_restrictions,
+                       $row->oarc_wiki,
+                       $row->oarc_rsa_key,
+                       $row->oarc_stage,
+                       $row->oarc_deleted
+               );
+       }
+
+}
+
diff --git a/backend/MWOAuthDataStore.php b/backend/MWOAuthDataStore.php
new file mode 100644
index 0000000..9d55973
--- /dev/null
+++ b/backend/MWOAuthDataStore.php
@@ -0,0 +1,215 @@
+<?php
+
+
+class MWOAuthDataStore extends OAuthDataStore {
+
+       // ObjectCache for Tokens and Nonces
+       protected $cache;
+
+       // Persistant storage for logging/audit
+       protected $logging;
+
+       public function __construct( BagOStuff $cache, $logdb ) {
+               $this->cache = $cache;
+               $this->loggging = $logdb;
+       }
+
+       function lookup_consumer( $consumer_key ) {
+               $dbr = $this->getSlaveDB();
+               $row = $dbr->selectRow(
+                       'oauth_registered_consumer',
+                       '*',
+                       array( 'oarc_consumer_key' => $consumer_key ),
+                       __METHOD__
+               );
+               if ( !$row ) {
+                       throw new MWOAuthException( 'invalid-client-key' );
+               }
+               return MWOAuthConsumer::newFromRow( $row );
+       }
+
+       /**
+        *
+        * @param $consumer
+        * @param $token_type
+        * @param $token String the token
+        */
+       function lookup_token( $consumer, $token_type, $token ) {
+               wfDebugLog( 'OAuth', __METHOD__ . ": Looking up $token_type 
token '$token'" );
+
+               if ( $token_type == 'request' ) {
+                       $returnToken = $this->cache->get( 
MWOAuthUtils::getCacheKey( 'token', $consumer->key, $token_type, $token ) );
+                       if ( $token === null ) {
+                               throw new MWOAuthException( 
'request-token-not-found' );
+                       }
+               } elseif ( $token_type == 'access' ) {
+                       $where = array(
+                               'oaac_access_token' => $token,
+                       );
+                       $dbr = $this->getSlaveDB();
+                       $row = $dbr->selectRow(
+                               'oauth_accepted_consumer',
+                               '*',
+                               $where,
+                               __METHOD__
+                       );
+
+                       if ( !$row ) {
+                               throw new MWOAuthException( 
'access-token-not-found' );
+                       }
+
+                       $returnToken = new MWOAuthToken( 
$row->oaac_access_token, $row->oaac_access_secret );
+               } else {
+                       throw new MWOAuthException( 'invalid-token-type' );
+               }
+
+               return $returnToken;
+       }
+
+       /**
+        * Check that nonce has not been seen before. Add it on check, so we 
don't repeat it.
+        * Note, timestamp has already been checked, so this should be a fresh 
nonce.
+        *
+        */
+       function lookup_nonce( $consumer, $token, $nonce, $timestamp ) {
+               $key = MWOAuthUtils::getCacheKey( 'nonce', $consumer->key, 
$token );
+               if ( $this->cache->get( $key ) ) {
+                       return true;
+               }
+               // Set 5 minutes in the future of the timestamp, to match 
OAuthServer. Use the
+               // timestamp so the client can also expire their nonce records 
after 5 mins.
+               $this->cache->set( $key, 1, $timestamp + 300 );
+               return false;
+       }
+
+       /**
+        * Generate and return an MWOAuthToken
+        * @return MWOAuthToken
+        */
+       protected function newToken() {
+               return new MWOAuthToken(
+                       MWCryptRand::generateHex( 32, false), //The key doesn't 
need to be unpredictable
+                       MWCryptRand::generateHex( 32, true)
+               );
+       }
+
+       /**
+        * Generate a new token, save it in the cache, and return it
+        */
+       function new_request_token( $consumer, $callback = null ) {
+               // return a new token attached to this consumer
+               $token = $this->newToken();
+               $cacheKey = MWOAuthUtils::getCacheKey( 'token', $consumer->key, 
'request', $token->key );
+               $this->cache->add( $cacheKey, $token, 600 ); //10 minutes. 
Kindof arbitray.
+               wfDebugLog( 'OAuth', __METHOD__ . ": New request token 
{$token->key} for {$consumer->key}" );
+               return $token;
+       }
+
+       /**
+        *
+        *
+        * @param MWOAuthToken $token the request token that started this
+        * @param OAuthConsumer $consumer
+        * @param $verifier
+        * @return OAuthToken the access token
+        */
+       function new_access_token( $token, $consumer, $verifier = null ) {
+               // return a new access token attached to this consumer
+               // for the user associated with this token if the request token
+               // is authorized
+               // should also invalidate the request token
+
+               if ( !$token->code || !$token->accessTokenKey ) {
+                       throw new MWOAuthException( 'bad-token' );
+               }
+
+               if ( $token->code !== $verifier ) {
+                       throw new MWOAuthException( 'bad-verifier' );
+               }
+
+               $accessToken = lookup_token( $consumer, 'access', 
$token->accessTokenKey );
+               $this->cache->delete( MWOAuthUtils::getCacheKey( 'token', 
$consumer->key, 'request', $token->key ) );
+               wfDebugLog( 'OAuth', __METHOD__ . ": New access token 
{$accessToken->key} for {$consumer->key}" );
+               return $accessToken;
+       }
+
+
+       public function getGrantsForConsumer( MWOAuthConsumer $consumer ) {
+               $grants = array();
+               $dbr = $this->getSlaveDB();
+               $rows = $dbr->select(
+                       'oauth_required_grant',
+                       '*',
+                       array( 'oarg_consumer_id' => $consumer->id ),
+                       __METHOD__
+               );
+               foreach ( $rows as $row ) {
+                       $grants[] = $row->oarg_grant;
+               }
+               return $grants;
+       }
+
+
+       /**
+        *
+        * @param $user User (or CentralAuthUser) who is authorizing this 
consumer
+        * @param OAuthConsumer $consumer the Consumer
+        * @param String $tokenKey the request token to update
+        * @return String verification code
+        */
+       public function authorizeRequest( $user, $consumer, $tokenKey ) {
+               wfDebugLog( 'OAuth', __METHOD__ . ": User " . $user->getName() 
. " Authorizing {$consumer->name}" );
+               $accessToken = $this->newToken();
+               $this->addGrant( $user, $consumer, $accessToken );
+
+               $cacheKey = MWOAuthUtils::getCacheKey( 'token', $consumer->key, 
'request', $tokenKey );
+               $reqToken = $this->cache->get( $cacheKey );
+               if ( !$reqToken ) {
+                       throw new MWOAuthException( 'bad-token' );
+               }
+               $code = MWCryptRand::generateHex( 32, true);
+               $reqToken->addVerifyCode( $code );
+               $reqToken->addAccessKey( $accessToken->key );
+
+               $this->cache->set( $cacheKey, $reqToken, 600 );
+
+               return $code;
+       }
+
+
+       /**
+        *
+        * @param $user User granting access
+        * @param MWOAuthConsumer $consumer 
+        * @param OAuthToken $token Credential/Access Token
+        */
+       private function addGrant( $user, MWOAuthConsumer $consumer, $token ) {
+               // insert into oauth_accepted_consumer
+               if ( $user->getId() == 0 ) {
+                       throw new MWOAuthExeption( 'invalid-user' );
+               }
+
+               $dbw = $this->getDB();
+               $row = array(
+                       'oaac_wiki' => $consumer->wiki,
+                       'oaac_user_id' => $user->getId(),
+                       'oaac_consumer_id' => $consumer->id,
+                       'oaac_access_token' => $token->key,
+                       'oaac_access_secret' => $token->secret,
+                       'oaac_accepted' => $dbw->timestamp( wfTimestampNow() )
+               );
+               $dbw->insert( 'oauth_accepted_consumer', $row, __METHOD__ );
+       }
+
+       private function getDB() {
+               global $wgOAuthStorageDB;
+               //return wfGetLB( $wgOAuthStorageDB )->getConnection( 
DB_MASTER, array(), $wgOAuthStorageDB );
+               return wfGetDB( DB_MASTER );
+       }
+
+       private function getSlaveDB() {
+               global $wgOAuthStorageDB;
+               //return wfGetLB( $wgOAuthStorageDB )->getConnection( DB_SLAVE, 
array(), $wgOAuthStorageDB );
+               return wfGetDB( DB_MASTER );
+       }
+}
diff --git a/backend/MWOAuthException.php b/backend/MWOAuthException.php
new file mode 100644
index 0000000..91e3ca5
--- /dev/null
+++ b/backend/MWOAuthException.php
@@ -0,0 +1,5 @@
+<?php
+
+class MWOAuthException extends OAuthException {
+
+}
diff --git a/backend/MWOAuthRequest.php b/backend/MWOAuthRequest.php
new file mode 100644
index 0000000..2cef49d
--- /dev/null
+++ b/backend/MWOAuthRequest.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * @file
+ * @ingroup OAuth
+ *
+ * @licence GNU GPL v2+
+ * @author Chris Steipp
+ */
+
+class MWOAuthRequest extends OAuthRequest {
+
+       public function getConsumerKey() {
+               $key = '';
+               if ( isset( $this->parameters['oauth_consumer_key'] ) ) {
+                       $key = $this->parameters['oauth_consumer_key'];
+               }
+               return $key;
+       }
+
+       public static function fromRequest( WebRequest $request ) {
+               $httpMethod = strtoupper( $request->getMethod() );
+               $httpUrl = $request->getRequestURL();
+
+               // Find request headers
+               $requestHeaders = MWOAuthUtils::getHeaders();
+
+               // Parse the query-string to find GET parameters
+               $parameters = $request->getQueryValues();
+
+               // It's a POST request of the proper content-type, so parse POST
+               // parameters and add those overriding any duplicates from GET
+               if ( $request->wasPosted()
+                       && isset( $requestHeaders['Content-Type'] )
+                       && strstr(
+                               $requestHeaders['Content-Type'],
+                               'application/x-www-form-urlencoded'
+                       )
+               ) {
+                       $postData = $request->getPostValues();
+                       $parameters = array_merge( $parameters, $postData );
+               }
+
+               // We have a Authorization-header with OAuth data. Parse the 
header
+               // and add those overriding any duplicates from GET or POST
+               if ( isset( $requestHeaders['Authorization'] )
+                       && substr( $requestHeaders['Authorization'], 0, 6 ) == 
'OAuth '
+               ) {
+                       $headerParameters = OAuthUtil::split_header(
+                               $requestHeaders['Authorization']
+                       );
+                       $parameters = array_merge($parameters, 
$headerParameters);
+               }
+               wfDebugLog( 'OAuth', __METHOD__ . ": parameters:\n" . print_r( 
$parameters, true) );
+               return new self( $httpMethod, $httpUrl, $parameters );
+       }
+
+       /**
+        * @param String|null $http_method
+        * @param String|null $http_url
+        * @param String|null $paramters pass in parameters instead of getting 
them from the request
+        * @return OAuthRequest
+        */
+       public static function from_request( $http_method = NULL, $http_url = 
NULL, $parameters = NULL) {
+               wfDebug( __METHOD__ . " called" );
+               global $wgRequest;
+
+               if ( $http_method === null ) {
+                       $http_method = strtoupper( $wgRequest->getMethod() );
+               }
+
+               if ( $http_url === null ) {
+                       $http_url = $wgRequest->getRequestURL();
+               }
+
+               if ( $parameters === null ) {
+
+                       // Find request headers
+                       $request_headers = MWOAuthUtil::getHeaders();
+
+                       // Parse the query-string to find GET parameters
+                       $parameters = $wgRequest->getQueryValues();
+
+                       // It's a POST request of the proper content-type, so 
parse POST
+                       // parameters and add those overriding any duplicates 
from GET
+                       if ( $wgRequest->wasPosted()
+                               && isset( $request_headers['Content-Type'] )
+                               && strstr(
+                                       $request_headers['Content-Type'],
+                                       'application/x-www-form-urlencoded'
+                               )
+                       ) {
+                               $post_data = $wgRequest->getPostValues();
+                               $parameters = array_merge($parameters, 
$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( $request_headers['Authorization'] )
+                               && substr( $request_headers['Authorization'], 
0, 6 ) == 'OAuth '
+                       ) {
+                               $header_parameters = OAuthUtil::split_header(
+                                       $request_headers['Authorization']
+                               );
+                               $parameters = array_merge($parameters, 
$header_parameters);
+                       }
+               }
+
+               wfDebugLog( 'OAuth', __METHOD__ . ": parameters:\n" . print_r( 
$parameters, true) );
+               return new self( $http_method, $http_url, $parameters );
+       }
+
+}
diff --git a/backend/MWOAuthSignatureMethod.php 
b/backend/MWOAuthSignatureMethod.php
new file mode 100644
index 0000000..d9e5272
--- /dev/null
+++ b/backend/MWOAuthSignatureMethod.php
@@ -0,0 +1,29 @@
+<?php
+
+class MWOAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod_RSA_SHA1 {
+
+       protected $store;
+
+       function __construct( OAuthDataStore $store ) {
+               $this->store = $store;
+       }
+
+
+       // Up to the SP to implement this lookup of keys. Possible ideas are:
+       // ( 1 ) do a lookup in a table of trusted certs keyed off of consumer
+       // ( 2 ) fetch via http using a url provided by the requester
+       // ( 3 ) some sort of specific discovery code based on request
+       //
+       // Either way should return a string representation of the certificate
+       protected function fetch_public_cert( &$request ) {
+
+       }
+
+       // Up to the SP to implement this lookup of keys. Possible ideas are:
+       // ( 1 ) do a lookup in a table of trusted certs keyed off of consumer
+       //
+       // Either way should return a string representation of the certificate
+       protected function fetch_private_cert( &$request ) {
+
+       }
+}
diff --git a/backend/MWOAuthToken.php b/backend/MWOAuthToken.php
new file mode 100644
index 0000000..f8e1e98
--- /dev/null
+++ b/backend/MWOAuthToken.php
@@ -0,0 +1,21 @@
+<?php
+
+
+class MWOAuthToken extends OAuthToken {
+
+       // Keep the verification code here
+       public $code;
+
+       // Token to find grant in oauth_accepted_consumer
+       public $accessTokenKey;
+
+       public function addVerifyCode( $code ) {
+               $this->code = $code;
+       }
+
+       public function addAccessKey( $key ) {
+               $this->accessTokenKey = $key;
+       }
+}
+
+
diff --git a/backend/MWOAuthUtils.php b/backend/MWOAuthUtils.php
new file mode 100644
index 0000000..ddbad36
--- /dev/null
+++ b/backend/MWOAuthUtils.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Static utility functions for OAuth
+ * @file
+ * @ingroup OAuth
+ *
+ * @licence GNU GPL v2+
+ * @author Chris Steipp
+ */
+class MWOAuthUtils extends OAuthUtil {
+
+       public static function getHeaders() {
+               global $wgRequest;
+
+               $headers = $wgRequest->getAllHeaders();
+
+               // 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;
+               }
+
+               return $out;
+       }
+
+
+       public static function getCacheKey() {
+               $args = func_get_args();
+               return 'OAUTH:' . implode( ':', $args );
+       }
+}
diff --git a/frontend/OAuthUI.setup.php b/frontend/OAuthUI.setup.php
index c5d0c58..931a1ab 100644
--- a/frontend/OAuthUI.setup.php
+++ b/frontend/OAuthUI.setup.php
@@ -10,7 +10,7 @@
         * @return void
         */
        public static function defineSpecialPages( array &$pages, array 
&$groups ) {
-
+               $pages['OAuth'] = 'SpecialOAuth';
        }
 
        /**
diff --git a/frontend/specials/SpecialOAuth.php 
b/frontend/specials/SpecialOAuth.php
new file mode 100644
index 0000000..12f3fad
--- /dev/null
+++ b/frontend/specials/SpecialOAuth.php
@@ -0,0 +1,131 @@
+<?php
+
+class SpecialOAuth extends UnlistedSpecialPage {
+
+       function __construct() {
+               parent::__construct( 'OAuth' );
+       }
+
+       public function execute( $subpage ) {
+
+               $request = $this->getRequest();
+               $format = $request->getVal( 'format', 'raw' );
+               if ( !in_array( $subpage, array( 'initiate', 'authorize', 
'token' ) ) ) {
+                       $this->showError( 'oauth-client-invalidrequest', 
$format );
+               }
+
+               try {
+
+                       $store = $this->getStorage();
+                       $oauthServer = new OAuthServer( $store );
+                       $oauthServer->add_signature_method( new 
OAuthSignatureMethod_HMAC_SHA1() );
+                       $oauthServer->add_signature_method( new 
MWOAuthSignatureMethod_RSA_SHA1( $store ) );
+
+                       switch ( $subpage ) {
+                               case 'initiate':
+                                       $OAuthRequest = 
MWOAuthRequest::fromRequest( $request );
+                                       wfDebugLog( 'OAuth', __METHOD__ . ": 
Consumer '{$OAuthRequest->getConsumerKey()}' getting temporary credentials" );
+                                       // fetch_request_token does the 
version, freshness, and sig checks
+                                       $token = 
$oauthServer->fetch_request_token( $OAuthRequest );
+                                       $this->returnToken( $token, $format );
+                                       break;
+                               case 'authorize':
+                                       //TODO
+                                       if ( $request->getVal( 'doAuthorize', 
false ) ) {
+                                               // Check csrf token
+                                               $CSRFTOken = $request->getVal( 
'formToken', false );
+                                               if ( 
!$this->getUser()->matchEditToken( $CSRFTOken, 'OAuth:Authorize' ) ) {
+                                                       throw new 
MWOAuthException( 'bad-csrf-token' );
+                                               }
+                                               // Create Grant
+                                               
+                                               // Redirect to callback url
+
+                                       } else {
+                                               $this->showAuthorizeForm( 
$request->getVal( 'oauth_token', false ) );
+                                       }
+                                       break;
+                               case 'token':
+                                       //TODO
+                                       break;
+                               default:
+                                       throw new OAuthException();
+                       }
+
+               } catch ( OAuthException $exception ) {
+                       wfDebugLog( 'OAuth', __METHOD__ . ": Exception 
{$exception->getMessage()}" );
+                       $this->showError( $exception->getMessage(), $format );
+               }
+       }
+
+       private function getStorage() {
+               global $wgMemc; //TODO instance of config
+               return new MWOAuthDataStore( $wgMemc, wfGetDB( DB_MASTER ) );
+       }
+
+       /**
+        *
+        *
+        * @param string $message message key to return to the user
+        * @param string $format the format of the response: json, xml, or html
+        */
+       private function showError( $message, $format ) {
+               if ( $format == 'html' ) {
+                       $this->getOutput()->showErrorPage( 'oauth-error', 
$message );
+               } elseif ( $format == 'raw' ) {
+                       $this->showResponse( 'Error: ' . wfMessage( $message ), 
'raw' );
+               } elseif ( $format == 'json' ) {
+                       $error = json_encode( array( 'error' => wfMessage( 
$message ) ) );
+                       $this->showResponse( $error, 'raw' );
+               }
+       }
+
+       /**
+        *
+        *
+        * @param array $response values to give back to the client
+        * @param string $format the format of the response: json, xml, or html
+        */
+       private function returnToken( OAuthToken $token, $format  ) {
+               if ( $format == 'raw' ) {
+                       $return = 'oauth_token=' . 
OAuthUtil::urlencode_rfc3986( $token->key );
+                       $return .= '&oauth_token_secret=' . 
OAuthUtil::urlencode_rfc3986( $token->secret );
+                       $this->showResponse( $return, 'raw' );
+               } elseif ( $format == 'json' ) {
+                       $this->showResponse( json_encode( $token ), 'raw' );
+               } elseif ( $format == 'html' ) {
+                       $html = Html::element(
+                               'li',
+                               array(),
+                               'oauth_token = ' . 
OAuthUtil::urlencode_rfc3986( $token->key )
+                       );
+                       $html .= Html::element(
+                               'li',
+                               array(),
+                               'oauth_token_secret = ' . 
OAuthUtil::urlencode_rfc3986( $token->secret )
+                       );
+                       $html = Html::rawElement( 'ul', array(), $html );
+                       $this->showResponse( $html, 'html' );
+               }
+       }
+
+
+       /**
+        *
+        *
+        * @param string $response html or string to pass back to the user. 
Already escaped.
+        * @param string $format the format of the response: raw, or otherwise
+        */
+       private function showResponse( $response, $format  ) {
+               $out = $this->getOutput();
+               if ( $format == 'raw' ) {
+                       $out->setArticleBodyOnly( true );
+                       $out->enableClientCache( false );
+                       $out->preventClickjacking();
+                       $out->clearHTML();
+                       $out->addHTML( $response );
+               } else {
+                       $out->addHtml( $response );
+               }
+       }
+}
diff --git a/lib/OAuth.php b/lib/OAuth.php
index e67402d..70fb953 100644
--- a/lib/OAuth.php
+++ b/lib/OAuth.php
@@ -32,6 +32,7 @@
 class OAuthConsumer {
        public $key;
        public $secret;
+       public $callback_url;
 
        function __construct( $key, $secret, $callback_url = NULL ) {
                $this->key = $key;
@@ -106,8 +107,9 @@
         * @return bool
         */
        public function check_signature( $request, $consumer, $token, 
$signature ) {
+               wfDebugLog( 'OAuth', __METHOD__ . ": Expecting: '$signature'" );
                $built = $this->build_signature( $request, $consumer, $token );
-
+               wfDebugLog( 'OAuth', __METHOD__ . ": Built: '$built'" );
                // Check for zero length, although unlikely here
                if ( strlen( $built ) == 0 || strlen( $signature ) == 0 ) {
                        return false;
@@ -141,6 +143,7 @@
 
        public function build_signature( $request, $consumer, $token ) {
                $base_string = $request->get_signature_base_string();
+               wfDebugLog( 'OAuth', __METHOD__ . ": Base string: 
'$base_string'" );
                $request->base_string = $base_string;
 
                $key_parts = array(
@@ -150,7 +153,7 @@
 
                $key_parts = OAuthUtil::urlencode_rfc3986( $key_parts );
                $key = implode( '&', $key_parts );
-
+               wfDebugLog( 'OAuth', __METHOD__ . ": HMAC Key: '$key'" );
                return base64_encode( hash_hmac( 'sha1', $base_string, $key, 
true ) );
        }
 }
@@ -265,8 +268,11 @@
        public static $POST_INPUT = 'php://input';
 
        function __construct( $http_method, $http_url, $parameters = NULL ) {
-               $parameters = ( $parameters ) ? $parameters : array();
-               $parameters = array_merge( OAuthUtil::parse_parameters( 
parse_url( $http_url, PHP_URL_QUERY ) ), $parameters );
+               # This was double-adding query parameters when from_request was 
used.
+               if ( !$parameters ) {
+                       $parameters = array();
+                       $parameters = array_merge( OAuthUtil::parse_parameters( 
parse_url( $http_url, PHP_URL_QUERY ) ), $parameters );
+               }
                $this->parameters = $parameters;
                $this->http_method = $http_method;
                $this->http_url = $http_url;
@@ -329,10 +335,10 @@
         */
        public static function from_consumer_and_token( $consumer, $token, 
$http_method, $http_url, $parameters = NULL ) {
                $parameters = ( $parameters ) ? $parameters : array();
-               $defaults = array( "oauth_version" = > OAuthRequest::$version,
-                       "oauth_nonce" = > OAuthRequest::generate_nonce(),
-                       "oauth_timestamp" = > 
OAuthRequest::generate_timestamp(),
-                       "oauth_consumer_key" = > $consumer->key );
+               $defaults = array( "oauth_version" => OAuthRequest::$version,
+                       "oauth_nonce" => OAuthRequest::generate_nonce(),
+                       "oauth_timestamp" => OAuthRequest::generate_timestamp(),
+                       "oauth_consumer_key" => $consumer->key );
                if ( $token ) {
                        $defaults['oauth_token'] = $token->key;
                }
@@ -394,6 +400,7 @@
         * and the concated with &.
         */
        public function get_signature_base_string() {
+               //wfDebugLog( 'OAuth', __METHOD__ . ": Generating base string 
when this->paramters:\n" . print_r( $this->parameters, true ) );
                $parts = array(
                        $this->get_normalized_http_method(),
                        $this->get_normalized_http_url(),
@@ -438,7 +445,7 @@
                $post_data = $this->to_postdata();
                $out = $this->get_normalized_http_url();
                if ( $post_data ) {
-                       $out . = '?'.$post_data;
+                       $out .= '?'.$post_data;
                }
                return $out;
        }
@@ -462,13 +469,13 @@
                        $out = 'Authorization: OAuth';
 
                $total = array();
-               foreach ( $this->parameters as $k = > $v ) {
+               foreach ( $this->parameters as $k => $v ) {
                        if ( substr( $k, 0, 5 ) != "oauth" ) continue;
                        if ( is_array( $v ) ) {
                                throw new OAuthException( 'Arrays not supported 
in headers' );
                        }
-                       $out . = ( $first ) ? ' ' : ',';
-                       $out . = OAuthUtil::urlencode_rfc3986( $k ) .
+                       $out .= ( $first ) ? ' ' : ',';
+                       $out .= OAuthUtil::urlencode_rfc3986( $k ) .
                                ' = "' .
                                OAuthUtil::urlencode_rfc3986( $v ) .
                                '"';
@@ -638,7 +645,7 @@
                if ( !$consumer_key ) {
                        throw new OAuthException( "Invalid consumer key" );
                }
-
+               wfDebugLog( 'OAuth', __METHOD__ . ": getting consumer for 
'$consumer_key'" );
                $consumer = $this->data_store->lookup_consumer( $consumer_key );
                if ( !$consumer ) {
                        throw new OAuthException( "Invalid consumer" );
@@ -681,7 +688,6 @@
                $this->check_nonce( $consumer, $token, $nonce, $timestamp );
 
                $signature_method = $this->get_signature_method( $request );
-
                $signature = $request->get_parameter( 'oauth_signature' );
                $valid_sig = $signature_method->check_signature(
                        $request,
@@ -691,6 +697,7 @@
                 );
 
                if ( !$valid_sig ) {
+                       wfDebugLog( 'OAuth', __METHOD__ . ": Signature check 
($signature_method) failed" );
                        throw new OAuthException( "Invalid signature" );
                }
        }
@@ -764,18 +771,18 @@
 
 class OAuthUtil {
        public static function urlencode_rfc3986( $input ) {
-       if ( is_array( $input ) ) {
-               return array_map( array( 'OAuthUtil', 'urlencode_rfc3986' ), 
$input );
-       } else if ( is_scalar( $input ) ) {
-               return str_replace(
-                       '+',
-                       ' ',
-                       str_replace( '%7E', '~', rawurlencode( $input ) )
-                );
-       } else {
-               return '';
+               if ( is_array( $input ) ) {
+                       return array_map( array( 'OAuthUtil', 
'urlencode_rfc3986' ), $input );
+               } else if ( is_scalar( $input ) ) {
+                       return str_replace(
+                               '+',
+                               ' ',
+                               str_replace( '%7E', '~', rawurlencode( $input ) 
)
+                        );
+               } else {
+                       return '';
+               }
        }
-}
 
 
        // This decode function isn't taking into consideration the above
@@ -793,7 +800,7 @@
        public static function split_header( $header, 
$only_allow_oauth_parameters = true ) {
                $params = array();
                if ( preg_match_all( '/( ' . ( $only_allow_oauth_parameters ? 
'oauth_' : '' ) . '[a-z_-]* ) = ( :?"( [^"]* )"|( [^,]* ) )/', $header, 
$matches ) ) {
-                       foreach ( $matches[1] as $i = > $h ) {
+                       foreach ( $matches[1] as $i => $h ) {
                                $params[$h] = OAuthUtil::urldecode_rfc3986( 
empty( $matches[3][$i] ) ? $matches[4][$i] : $matches[3][$i] );
                        }
                        if ( isset( $params['realm'] ) ) {
@@ -815,7 +822,7 @@
                        // returns the headers in the same case as they are in 
the
                        // request
                        $out = array();
-                       foreach ( $headers AS $key = > $value ) {
+                       foreach ( $headers AS $key => $value ) {
                                $key = str_replace(
                                        " ",
                                        "-",
@@ -832,7 +839,7 @@
                        if( isset( $_ENV['CONTENT_TYPE'] ) )
                                $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
 
-                       foreach ( $_SERVER as $key = > $value ) {
+                       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
@@ -851,7 +858,7 @@
 
        // 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' )
+       // array( 'a' => array( 'b','c' ), 'd' => 'e' )
        public static function parse_parameters( $input ) {
                if ( !isset( $input ) || !$input ) return array();
 
@@ -882,6 +889,7 @@
        }
 
        public static function build_http_query( $params ) {
+               wfDebugLog( 'OAuth', __METHOD__ . " called with params:\n" . 
print_r( $params, true ) );
                if ( !$params ) return '';
 
                // Urlencode both keys and values
@@ -894,17 +902,17 @@
                uksort( $params, 'strcmp' );
 
                $pairs = array();
-               foreach ( $params as $parameter = > $value ) {
+               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 )
                                // June 12th, 2010 - changed to sort because of 
issue 164 by hidetaka
                                sort( $value, SORT_STRING );
                                foreach ( $value as $duplicate_value ) {
-                                       $pairs[] = $parameter . ' = ' . 
$duplicate_value;
+                                       $pairs[] = $parameter . '=' . 
$duplicate_value;
                                }
                        } else {
-                               $pairs[] = $parameter . ' = ' . $value;
+                               $pairs[] = $parameter . '=' . $value;
                        }
                }
                // For each parameter, the name is separated from the 
corresponding value by an ' = ' character ( ASCII code 61 )

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib6dd320b58f7ba2e4df3a8545cf9f3a790c2fa9b
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/OAuth
Gerrit-Branch: master
Gerrit-Owner: CSteipp <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to