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