Parent5446 has submitted this change and it was merged.

Change subject: Fix extension functionality and backwards compatibility
......................................................................


Fix extension functionality and backwards compatibility

This updates the CA certificate and login endpoint to match Mozilla.
Also changes some things to make the extension backwards-compatible
with version 1.19 of MediaWiki.

Finally, this fixes a few minor bugs:
* Infinite looping when authentication is unsuccessful
* Non-internationalized error messages

Bug: 55975
Change-Id: If61cf139256e411440eb5a327f32f37bd6dfb962
---
M ApiPersona.php
M Persona.i18n.php
M Persona.php
D js/persona.js
M js/persona_hooks.js
A js/persona_hooks_old.js
M persona.crt
7 files changed, 340 insertions(+), 132 deletions(-)

Approvals:
  Parent5446: Verified; Looks good to me, approved



diff --git a/ApiPersona.php b/ApiPersona.php
index dfbaa5b..4283cad 100644
--- a/ApiPersona.php
+++ b/ApiPersona.php
@@ -8,12 +8,12 @@
  * 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.
-
+ *
  * Extension:Persona 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 Extension:Persona.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -114,7 +114,8 @@
 
        function execute() {
                global $wgSecureLogin;
-               if( $wgSecureLogin && WebRequest::detectProtocol() !== 'https' 
) {
+
+               if ( $wgSecureLogin && WebRequest::detectProtocol() !== 'https' 
) {
                        $this->dieUsage( 'Secure login is enabled, and an 
insecure (non-HTTPS) request was made.', 'insecure' );
                }
 
@@ -123,50 +124,62 @@
                // Check login token and throttling as is done in 
LoginForm::authenticateUserData.
                // Note that since we do not yet know the username of the login 
target, the throttle
                // is set for an empty user, effectively making this a per-IP 
only throttle.
-               if( !LoginForm::getLoginToken() ) {
+               if ( !LoginForm::getLoginToken() ) {
                        LoginForm::setLoginToken();
                        $this->dieUsageMsg( 'sessionfailure' );
-               } elseif( LoginForm::incLoginThrottle( '' ) === true ) {
+               } elseif ( LoginForm::incLoginThrottle( '' ) === true ) {
                        $this->dieUsageMsg( 'actionthrottledtext' );
-               } elseif( $params['token'] !== LoginForm::getLoginToken() ) {
+               } elseif ( $params['token'] !== LoginForm::getLoginToken() ) {
                        $this->dieUsageMsg( 'sessionfailure' );
                }
 
                // Contact the verification server.
                $assertion = $params['assertion'];
-               $response = Http::post(
-                       'https://verifier.login.persona.org/verify',
-                       array(
-                               'caInfo' => __DIR__ . '/persona.crt',
-                               'postData' => wfArrayToCgi( array(
-                                       'assertion' => $assertion,
-                                       'audience' => wfExpandUrl( '/', 
$wgSecureLogin ? PROTO_HTTPS : PROTO_HTTP )
-                               ) )
-                       )
-               );
-               $result = (array) FormatJson::decode( $response );
+               $request = MWHttpRequest::factory( 
'https://login.persona.org/verify', array(
+                       'method' => 'post',
+                       'caInfo' => __DIR__ . '/persona.crt',
+                       'sslVerifyHost' => true,
+                       'sslVerifyCert' => true,
+                       'postData' => wfArrayToCgi( array(
+                               'assertion' => $assertion,
+                               'audience' => wfExpandUrl( '/', $wgSecureLogin 
? PROTO_HTTPS : PROTO_HTTP )
+                       ) ),
+               ) );
+               $request->setHeader( 'Content-Type', 
'application/x-www-form-urlencoded' );
 
-               if( !isset( $result['status'] ) || $result['status'] !== 'okay' 
) {
+               $status = $request->execute();
+               $response = $request->getContent();
+               $result = (array)FormatJson::decode( $response );
+
+               if ( !isset( $result['status'] ) || $result['status'] !== 
'okay' ) {
                        // Bad assertion. Do nothing, as the response itself has
                        // sufficient information.
                        $result['status'] = 'failure';
-               } elseif( $result['audience'] != wfExpandUrl( '/', 
$wgSecureLogin ? PROTO_HTTPS : PROTO_HTTP ) ) {
+               } elseif ( $result['audience'] != wfExpandUrl( '/', 
$wgSecureLogin ? PROTO_HTTPS : PROTO_HTTP ) ) {
                        // Weird. Audience was returned differently.
                        $result['status'] = 'error';
                } else {
                        // Valid token. Login the user.
+
+                       // BC: User::selectFields is new in MediaWiki 1.21
+                       if ( function_exists( array( 'User', 'selectFields' ) ) 
) {
+                               $fields = User::selectFields();
+                       } else {
+                               $fields = '*';
+                       }
+
                        $dbr = wfGetDB( DB_MASTER );
                        $res = $dbr->select(
                                'user',
-                               User::selectFields(),
+                               $fields,
                                array( 'user_email' => $result['email'] )
                        );
 
-                       if( $res === false ) {
+                       if ( $res === false ) {
                                $result['status'] = 'dberror';
-                       } elseif( $res->numRows() == 0 ) {
+                       } elseif ( $res->numRows() == 0 ) {
                                $result['status'] = 'invaliduser';
-                       } elseif( $res->numRows() > 1 ) {
+                       } elseif ( $res->numRows() > 1 ) {
                                $result['status'] = 'multipleusers';
                        } else {
                                // We're good to go. Login the user.
@@ -182,7 +195,10 @@
                                }
 
                                LoginForm::clearLoginToken();
-                               ApiQueryInfo::resetTokenCache();
+                               // BC: ApiQueryInfo::resetTokenCache didn't 
exist in MediaWiki 1.20
+                               if ( function_exists( array( 'ApiQueryInfo', 
'resetTokenCache' ) ) ) {
+                                       ApiQueryInfo::resetTokenCache();
+                               }
 
                                // Add injected HTML as an optional message.
                                $injected_html = '';
diff --git a/Persona.i18n.php b/Persona.i18n.php
index e5d6d0e..6764e28 100644
--- a/Persona.i18n.php
+++ b/Persona.i18n.php
@@ -8,12 +8,12 @@
  * 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.
-
+ *
  * Extension:Persona 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 Extension:Persona.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -22,7 +22,12 @@
 
 $messages['en'] = array(
        'persona-login' => 'Login with Persona',
-       'persona-desc' => 'Allows users to log in with their Mozilla Persona 
account.'
+       'persona-desc' => 'Allows users to log in with their Mozilla Persona 
account.',
+       'persona-error-insecure' => 'Logging in over an insecure connection is 
not allowed.',
+       'persona-error-failure' => 'Persona failed to verify your identity.',
+       'persona-error-dberror' => 'An internal database error occurred.',
+       'persona-error-invaliduser' => 'There is no user on {{SITENAME}} 
matching your Persona account.',
+       'persona-error-multipleusers' => 'There are multiple users with the 
same email address as your Persona account. Your account must have a unique 
email address to log in with Persona.',
 );
 
 /** Message documentation (Message documentation)
@@ -32,6 +37,11 @@
 $messages['qqq'] = array(
        'persona-login' => 'Label for the Persona login link and button',
        'persona-desc' => 
'{{desc|name=Persona|url=http://www.mediawiki.org/wiki/Extension:Persona}}',
+       'persona-error-insecure' => 'Error message displayed using mw.notify 
when attempting insecure login with $wgSecureLogin enabled',
+       'persona-error-failure' => 'Error message displayed using mw.notify 
when the Persona API fails to verify',
+       'persona-error-dberror' => 'Error message displayed using mw.notify 
when an internal error occurs',
+       'persona-error-invaliduser' => 'Error message displayed using mw.notify 
for invalid logins',
+       'persona-error-multipleusers' => 'Error message displayed using 
mw.notify when a Persona account matches multiple MediaWiki accounts',
 );
 
 /** Asturian (asturianu)
diff --git a/Persona.php b/Persona.php
index cccf21a..6e053c2 100644
--- a/Persona.php
+++ b/Persona.php
@@ -8,12 +8,12 @@
  * 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.
-
+ *
  * Extension:Persona 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 Extension:Persona.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -27,6 +27,12 @@
        'descriptionmsg' => 'persona-desc'
 );
 
+/**
+ * Determines if the persona login link is displayed on every page in the 
personal URLs bar (true)
+ * or if it is only displayed on the login page (false).
+ *
+ * @var bool
+ */
 $wgPersonaLoginAnywhere = true;
 
 $wgHooks['BeforePageDisplay'][] = 'efAddPersonaModule';
@@ -37,37 +43,110 @@
 $wgExtensionMessagesFiles['Persona'] = __DIR__ . '/Persona.i18n.php';
 
 $wgResourceModules['ext.persona'] = array(
-       'scripts' => array( 'js/persona.js', 'js/persona_hooks.js' ),
-       'styles' => array(),
-       'messages' => array(),
-       'dependencies' => array( 'mediawiki.api', 'mediawiki.Title' ),
+       'scripts' => array( 'js/persona_hooks.js' ),
+       'messages' => array(
+               'sessionfailure',
+               'actionthrottledtext',
+               'persona-error-insecure',
+               'persona-error-failure',
+               'persona-error-dberror',
+               'persona-error-invaliduser',
+               'persona-error-multipleusers',
+       ),
+       'dependencies' => array(
+               'mediawiki.api',
+               'mediawiki.Title',
+               'mediawiki.notify',
+               'mediawiki.jqueryMsg',
+       ),
+       'localBasePath' => __DIR__,
+       'remoteExtPath' => 'Persona'
+);
+$wgResourceModules['ext.persona.old'] = array(
+       'scripts' => array( 'js/persona_hooks_old.js' ),
+       'messages' => array(
+               'sessionfailure',
+               'actionthrottledtext',
+               'persona-error-insecure',
+               'persona-error-failure',
+               'persona-error-dberror',
+               'persona-error-invaliduser',
+               'persona-error-multipleusers',
+       ),
+       'dependencies' => array(
+               'mediawiki.api',
+               'mediawiki.Title',
+               'mediawiki.notify',
+               'mediawiki.jqueryMsg',
+       ),
        'localBasePath' => __DIR__,
        'remoteExtPath' => 'Persona'
 );
 
 /**
- * Add the Persona module to the OutputPage.
+ * Add the Persona JS module and variables to the output page. Also make sure 
a session
+ * is started and a login token is set.
  *
- * @param &$out OutputPage object
- * @param &$skin Skin object
+ * @param User $user Current user that is logged in
+ * @param OutputPage $out Output page to add scripts to
  */
-function efAddPersonaModule( OutputPage &$out, Skin &$skin ) {
-       global $wgPersonaLoginAnywhere;
-       if( !$wgPersonaLoginAnywhere ) {
-               return true;
-       } elseif( !isset( $_SESSION ) ) {
+function efPersonaAddScripts( User $user, OutputPage $out ) {
+       global $wgVersion;
+
+       if ( !isset( $_SESSION ) ) {
                wfSetupSession();
        }
-
-       $out->addModules( 'ext.persona' );
-       if( !LoginForm::getLoginToken() ) {
+       if ( !LoginForm::getLoginToken() ) {
                LoginForm::setLoginToken();
        }
+
+       // Persona requires that IE compatibility mode be disabled
+       // Add the meta tag here in case MediaWiki core doesn't do it
+       $out->addMeta( 'http:X-UA-Compatible', 'IE=Edge' );
+
+       if ( ResourceLoader::inDebugMode() ) {
+               $out->addScriptFile( 
'https://login.persona.org/include.orig.js' );
+       } else {
+               $out->addScriptFile( 'https://login.persona.org/include.js' );
+       }
+
+       if ( version_compare( $wgVersion, '1.20', '<' ) ) {
+               $out->addModules( 'ext.persona.old' );
+       } else {
+               $out->addModules( 'ext.persona' );
+       }
+
+       $out->addJsConfigVars( 'wgPersonaUserEmail',
+               $user->isEmailConfirmed() ? $user->getEmail() : null );
+}
+
+/**
+ * Add the Persona module to the OutputPage.
+ *
+ * @param OutputPage &$out
+ *
+ * @return bool true
+ */
+function efAddPersonaModule( OutputPage &$out ) {
+       global $wgPersonaLoginAnywhere;
+
+       // Only add the modules and whatnot if necessary.
+       if (
+               !$wgPersonaLoginAnywhere &&
+               $out->getTitle()->equals( SpecialPage::getTitleFor( 'Userlogin' 
) )
+       ) {
+               return true;
+       }
+
+       $context = RequestContext::getMain();
+       efPersonaAddScripts( $context->getUser(), $out );
+
        $out->addHTML( Html::input(
                'wpLoginToken',
                LoginForm::getLoginToken(),
                'hidden'
        ) );
+
        return true;
 }
 
@@ -76,15 +155,21 @@
  * to the login form.
  *
  * @param $template QuickTemplate
+ *
+ * @return bool true
  */
 function efAddPersonaLogin( $template ) {
-       $context = RequestContext::getMain();
-       $out = $context->getOutput();
-       $out->addModules( 'ext.persona' );
-
-       $label = wfMessage( 'persona-login' )->escaped();
-       $personaButton = Html::input( 'wpPersona', $label, 'button', array( 
'id' => 'wpPersona' ) );
+       $personaButton = Html::input(
+               'wpPersona',
+               wfMessage( 'persona-login' )->text(),
+               'button',
+               array(
+                       'id' => 'wpPersona',
+                       'class' => 'mw-ui-button',
+               )
+       );
        $template->set( 'header', $personaButton );
+
        return true;
 }
 
@@ -93,19 +178,19 @@
  *
  * @param $personal_urls Array of personal URLs
  * @param $title Title currently being viewed
+ *
+ * @return bool true
  */
 function efAddPersonaLinks( array &$personal_urls, Title $title ) {
        global $wgPersonaLoginAnywhere;
-       if( $wgPersonaLoginAnywhere && !isset( $personal_urls['logout'] ) ) {
-               $context = RequestContext::getMain();
-               $out = $context->getOutput();
-               $out->addModules( 'ext.persona' );
 
+       if ( $wgPersonaLoginAnywhere && !isset( $personal_urls['logout'] ) ) {
                $personal_urls['personalogin'] = array(
                        'text' => wfMessage( 'persona-login' ),
                        'href' => '#',
                        'active' => $title->isSpecial( 'Userlogin' )
                );
        }
+
        return true;
 }
diff --git a/js/persona.js b/js/persona.js
deleted file mode 100644
index d005a70..0000000
--- a/js/persona.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Uncompressed source can be found at 
https://login.persona.org/include.orig.js
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-(function(){var a=function(){function e(a){return 
Array.isArray?Array.isArray(a):a.constructor.toString().indexOf("Array")!=-1}function
 d(a,c,d){var e=b[c][d];for(var 
f=0;f<e.length;f++)e[f].win===a&&e.splice(f,1);b[c][d].length===0&&delete 
b[c][d]}function c(a,c,d,e){function f(b){for(var 
c=0;c<b.length;c++)if(b[c].win===a)return!0;return!1}var 
g=!1;if(c==="*")for(var h in 
b){if(!b.hasOwnProperty(h))continue;if(h==="*")continue;if(typeof 
b[h][d]=="object"){g=f(b[h][d]);if(g)break}}else 
b["*"]&&b["*"][d]&&(g=f(b["*"][d])),!g&&b[c]&&b[c][d]&&(g=f(b[c][d]));if(g)throw"A
 channel is already bound to the same window which overlaps with origin '"+c+"' 
and has scope '"+d+"'";typeof b[c]!="object"&&(b[c]={}),typeof 
b[c][d]!="object"&&(b[c][d]=[]),b[c][d].push({win:a,handler:e})}"use 
strict";var a=Math.floor(Math.random()*1000001),b={},f={},g=function(a){try{var 
c=JSON.parse(a.data);if(typeof 
c!="object"||c===null)throw"malformed"}catch(a){return}var 
d=a.source,e=a.origin,g,h,i;if(typeof c.method=="string"){var 
j=c.method.split("::");j.length==2?(g=j[0],i=j[1]):i=c.method}typeof 
c.id!="undefined"&&(h=c.id);if(typeof i=="string"){var 
k=!1;if(b[e]&&b[e][g])for(var 
l=0;l<b[e][g].length;l++)if(b[e][g][l].win===d){b[e][g][l].handler(e,i,c),k=!0;break}if(!k&&b["*"]&&b["*"][g])for(var
 
l=0;l<b["*"][g].length;l++)if(b["*"][g][l].win===d){b["*"][g][l].handler(e,i,c);break}}else
 typeof 
h!="undefined"&&f[h]&&f[h](e,i,c)};window.addEventListener?window.addEventListener("message",g,!1):window.attachEvent&&window.attachEvent("onmessage",g);return{build:function(b){var
 g=function(a){if(b.debugOutput&&window.console&&window.console.log){try{typeof 
a!="string"&&(a=JSON.stringify(a))}catch(c){}console.log("["+j+"] 
"+a)}};if(!window.postMessage)throw"jschannel cannot run this browser, no 
postMessage";if(!window.JSON||!window.JSON.stringify||!window.JSON.parse)throw"jschannel
 cannot run this browser, no JSON parsing/serialization";if(typeof 
b!="object")throw"Channel build invoked without a proper object 
argument";if(!b.window||!b.window.postMessage)throw"Channel.build() called 
without a valid window argument";if(window===b.window)throw"target window is 
same as present window -- not allowed";var h=!1;if(typeof 
b.origin=="string"){var 
i;b.origin==="*"?h=!0:null!==(i=b.origin.match(/^https?:\/\/(?:[-a-zA-Z0-9_\.])+(?::\d+)?/))&&(b.origin=i[0].toLowerCase(),h=!0)}if(!h)throw"Channel.build()
 called with an invalid origin";if(typeof b.scope!="undefined"){if(typeof 
b.scope!="string")throw"scope, when specified, must be a 
string";if(b.scope.split("::").length>1)throw"scope may not contain double 
colons: '::'"}var j=function(){var 
a="",b="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";for(var 
c=0;c<5;c++)a+=b.charAt(Math.floor(Math.random()*b.length));return 
a}(),k={},l={},m={},n=!1,o=[],p=function(a,b,c){var 
d=!1,e=!1;return{origin:b,invoke:function(b,d){if(!m[a])throw"attempting to 
invoke a callback of a nonexistent transaction: "+a;var e=!1;for(var 
f=0;f<c.length;f++)if(b===c[f]){e=!0;break}if(!e)throw"request supports no such 
callback 
'"+b+"'";t({id:a,callback:b,params:d})},error:function(b,c){e=!0;if(!m[a])throw"error
 called for nonexistent message: "+a;delete 
m[a],t({id:a,error:b,message:c})},complete:function(b){e=!0;if(!m[a])throw"complete
 called for nonexistent message: "+a;delete 
m[a],t({id:a,result:b})},delayReturn:function(a){typeof 
a=="boolean"&&(d=a===!0);return d},completed:function(){return 
e}}},q=function(a,b,c){return window.setTimeout(function(){if(l[a]){var 
d="timeout ("+b+"ms) exceeded on method 
'"+c+"'";(1,l[a].error)("timeout_error",d),delete l[a],delete 
f[a]}},b)},r=function(a,c,d){if(typeof 
b.gotMessageObserver=="function")try{b.gotMessageObserver(a,d)}catch(h){g("gotMessageObserver()
 raised an exception: "+h.toString())}if(d.id&&c){if(k[c]){var 
i=p(d.id,a,d.callbacks?d.callbacks:[]);m[d.id]={};try{if(d.callbacks&&e(d.callbacks)&&d.callbacks.length>0)for(var
 j=0;j<d.callbacks.length;j++){var 
n=d.callbacks[j],o=d.params,q=n.split("/");for(var r=0;r<q.length-1;r++){var 
s=q[r];typeof o[s]!="object"&&(o[s]={}),o=o[s]}o[q[q.length-1]]=function(){var 
a=n;return function(b){return i.invoke(a,b)}}()}var 
t=k[c](i,d.params);!i.delayReturn()&&!i.completed()&&i.complete(t)}catch(h){var 
u="runtime_error",v=null;typeof h=="string"?v=h:typeof 
h=="object"&&(h&&e(h)&&h.length==2?(u=h[0],v=h[1]):typeof 
h.error=="string"&&(u=h.error,h.message?typeof 
h.message=="string"?v=h.message:h=h.message:v=""));if(v===null)try{v=JSON.stringify(h),typeof
 v=="undefined"&&(v=h.toString())}catch(w){v=h.toString()}i.error(u,v)}}}else 
d.id&&d.callback?!l[d.id]||!l[d.id].callbacks||!l[d.id].callbacks[d.callback]?g("ignoring
 invalid callback, id:"+d.id+" 
("+d.callback+")"):l[d.id].callbacks[d.callback](d.params):d.id?l[d.id]?(d.error?(1,l[d.id].error)(d.error,d.message):d.result!==undefined?(1,l[d.id].success)(d.result):(1,l[d.id].success)(),delete
 l[d.id],delete f[d.id]):g("ignoring invalid response: 
"+d.id):c&&k[c]&&k[c](null,d.params)};c(b.window,b.origin,typeof 
b.scope=="string"?b.scope:"",r);var s=function(a){typeof 
b.scope=="string"&&b.scope.length&&(a=[b.scope,a].join("::"));return 
a},t=function(a,c){if(!a)throw"postMessage called with null message";var 
d=n?"post  ":"queue ";g(d+" message: 
"+JSON.stringify(a));if(!c&&!n)o.push(a);else{if(typeof 
b.postMessageObserver=="function")try{b.postMessageObserver(b.origin,a)}catch(e){g("postMessageObserver()
 raised an exception: 
"+e.toString())}b.window.postMessage(JSON.stringify(a),b.origin)}},u=function(a,c){g("ready
 msg received");if(n)throw"received ready message while in ready state.  
help!";c==="ping"?j+="-R":j+="-L",v.unbind("__ready"),n=!0,g("ready msg 
accepted."),c==="ping"&&v.notify({method:"__ready",params:"pong"});while(o.length)t(o.pop());typeof
 b.onReady=="function"&&b.onReady(v)},v={unbind:function(a){if(k[a]){if(delete 
k[a])return!0;throw"can't delete method: 
"+a}return!1},bind:function(a,b){if(!a||typeof a!="string")throw"'method' 
argument to bind must be string";if(!b||typeof b!="function")throw"callback 
missing from bind params";if(k[a])throw"method '"+a+"' is already 
bound!";k[a]=b;return this},call:function(b){if(!b)throw"missing arguments to 
call function";if(!b.method||typeof b.method!="string")throw"'method' argument 
to call must be string";if(!b.success||typeof 
b.success!="function")throw"'success' callback missing from call";var 
c={},d=[],e=function(a,b){if(typeof b=="object")for(var f in 
b){if(!b.hasOwnProperty(f))continue;var g=a+(a.length?"/":"")+f;typeof 
b[f]=="function"?(c[g]=b[f],d.push(g),delete b[f]):typeof 
b[f]=="object"&&e(g,b[f])}};e("",b.params);var 
g={id:a,method:s(b.method),params:b.params};d.length&&(g.callbacks=d),b.timeout&&q(a,b.timeout,s(b.method)),l[a]={callbacks:c,error:b.error,success:b.success},f[a]=r,a++,t(g)},notify:function(a){if(!a)throw"missing
 arguments to notify function";if(!a.method||typeof 
a.method!="string")throw"'method' argument to notify must be 
string";t({method:s(a.method),params:a.params})},destroy:function(){d(b.window,b.origin,typeof
 
b.scope=="string"?b.scope:""),window.removeEventListener?window.removeEventListener("message",r,!1):window.detachEvent&&window.detachEvent("onmessage",r),n=!1,k={},m={},l={},b.origin=null,o=[],g("channel
 destroyed"),j=""}};v.bind("__ready",u),setTimeout(function(){},0);return 
v}}}();WinChan=function(){function i(){var 
b=window.location,c=window.opener.frames,d=b.protocol+"//"+b.host;for(var 
e=c.length-1;e>=0;e--)try{if(c[e].location.href.indexOf(d)===0&&c[e].name===a)return
 c[e]}catch(f){}return}function 
h(a){/^https?:\/\//.test(a)||(a=window.location.href);var 
b=/^(https?:\/\/[\-_a-zA-Z\.0-9:]+)/.exec(a);return b?b[1]:a}function 
g(){return 
window.JSON&&window.JSON.stringify&&window.JSON.parse&&window.postMessage}function
 f(){try{var a=navigator.userAgent;return 
a.indexOf("Fennec/")!=-1||a.indexOf("Firefox/")!=-1&&a.indexOf("Android")!=-1}catch(b){}return!1}function
 e(){var a=-1;if(navigator.appName==="Microsoft Internet Explorer"){var 
b=navigator.userAgent,c=new RegExp("MSIE 
([0-9]{1,}[.0-9]{0,})");c.exec(b)!=null&&(a=parseFloat(RegExp.$1))}return 
a>=8}function 
d(a,b,c){a.detachEvent?a.detachEvent("on"+b,c):a.removeEventListener&&a.removeEventListener(b,c,!1)}function
 
c(a,b,c){a.attachEvent?a.attachEvent("on"+b,c):a.addEventListener&&a.addEventListener(b,c,!1)}var
 a="__winchan_relay_frame",b="die",j=e();return 
g()?{open:function(e,g){function q(a){try{var 
b=JSON.parse(a.data);b.a==="ready"?m.postMessage(o,l):b.a==="error"?g&&(g(b.d),g=null):b.a==="response"&&(d(window,"message",q),d(window,"unload",p),p(),g&&(g(null,b.d),g=null))}catch(c){}}function
 
p(){k&&document.body.removeChild(k),k=undefined;if(n)try{n.close()}catch(a){m.postMessage(b,l)}n=m=undefined}if(!g)throw"missing
 required callback argument";var i;e.url||(i="missing required 'url' 
parameter"),e.relay_url||(i="missing required 'relay_url' 
parameter"),i&&setTimeout(function(){g(i)},0),e.window_name||(e.window_name=null);if(!e.window_features||f())e.window_features=undefined;var
 k,l=h(e.url);if(l!==h(e.relay_url))return setTimeout(function(){g("invalid 
arguments: origin of url and relay_url must match")},0);var 
m;j&&(k=document.createElement("iframe"),k.setAttribute("src",e.relay_url),k.style.display="none",k.setAttribute("name",a),document.body.appendChild(k),m=k.contentWindow);var
 n=window.open(e.url,e.window_name,e.window_features);m||(m=n);var 
o=JSON.stringify({a:"request",d:e.params});c(window,"unload",p),c(window,"message",q);return{close:p,focus:function(){if(n)try{n.focus()}catch(a){}}}}}:{open:function(a,b,c,d){setTimeout(function(){d("unsupported
 browser")},0)}}}();var b=function(){function l(){return c}function 
k(){c=g()||h()||i()||j();return!c}function 
j(){if(!(window.JSON&&window.JSON.stringify&&window.JSON.parse))return"JSON_NOT_SUPPORTED"}function
 i(){if(!a.postMessage)return"POSTMESSAGE_NOT_SUPPORTED"}function h(){try{var 
b="localStorage"in 
a&&a.localStorage!==null;if(b)a.localStorage.setItem("test","true"),a.localStorage.removeItem("test");else
 
return"LOCALSTORAGE_NOT_SUPPORTED"}catch(c){return"LOCALSTORAGE_DISABLED"}}function
 g(){return f()}function f(){var 
a=e(),b=a>-1&&a<8;if(b)return"BAD_IE_VERSION"}function e(){var 
a=-1;if(b.appName=="Microsoft Internet Explorer"){var c=b.userAgent,d=new 
RegExp("MSIE 
([0-9]{1,}[.0-9]{0,})");d.exec(c)!=null&&(a=parseFloat(RegExp.$1))}return 
a}function d(c,d){b=c,a=d}var 
a=window,b=navigator,c;return{setTestEnv:d,isSupported:k,getNoSupportReason:l}}();navigator.id||(navigator.id={});if(!navigator.id.request||navigator.id._shimmed){var
 
c="https://login.persona.org",d=navigator.userAgent,e=d.indexOf("Fennec/")!=-1||d.indexOf("Firefox/")!=-1&&d.indexOf("Android")!=-1,f=e?undefined:"menubar=0,location=1,resizable=1,scrollbars=1,status=0,dialog=1,minimizable=1,width=700,height=375",g,h={login:null,logout:null,ready:null},i=undefined;function
 j(a){a!==!0;if(i===undefined)i=a;else if(i!=a)throw"you cannot combine the 
navigator.id.watch() API with navigator.id.getVerifiedEmail() or 
navigator.id.get()this site should instead use navigator.id.request() and 
navigator.id.watch()"}var k,l=b.isSupported();function 
m(){if(!!l)try{if(!k){var 
b=window.document,d=b.createElement("iframe");d.style.display="none",b.body.appendChild(d),d.src=c+"/communication_iframe",k=a.build({window:d.contentWindow,origin:c,scope:"mozid_ni",onReady:function(){k.call({method:"loaded",success:function(){h.ready&&h.ready()},error:function(){}})}}),k.bind("logout",function(a,b){h.logout&&h.logout()}),k.bind("login",function(a,b){h.login&&h.login(b)})}}catch(e){k=undefined}}function
 n(a){return typeof a!="undefined"}function 
o(a){try{console.warn(a)}catch(b){}}function p(a,b){if(n(a[b])){o(b+" has been 
deprecated");return!0}}function q(a,b,c){if(n(a[b])&&n(a[c]))throw"you cannot 
supply *both* "+b+" and "+c;p(a,b)&&(a[c]=a[b],delete a[b])}function 
r(a){if(typeof a=="object"){if(a.onlogin&&typeof 
a.onlogin!="function"||a.onlogout&&typeof 
a.onlogout!="function"||a.onready&&typeof 
a.onready!="function")throw"non-function where function expected in parameters 
to navigator.id.watch()";if(!a.onlogin)throw"'onlogin' is a required argument 
to navigator.id.watch()";if(!a.onlogout)throw"'onlogout' is a required argument 
to 
navigator.id.watch()";h.login=a.onlogin||null,h.logout=a.onlogout||null,h.ready=a.onready||null,m(),q(a,"loggedInEmail","loggedInUser"),typeof
 
a.loggedInUser!="undefined"&&k&&k.notify({method:"loggedInUser",params:a.loggedInUser})}}function
 
s(a){p(a,"requiredEmail"),q(a,"tosURL","termsOfService"),q(a,"privacyURL","privacyPolicy"),a.termsOfService&&!a.privacyPolicy&&o("termsOfService
 ignored unless privacyPolicy also 
defined"),a.privacyPolicy&&!a.termsOfService&&o("privacyPolicy ignored unless 
termsOfService also 
defined");if(g)try{g.focus()}catch(d){}else{if(!b.isSupported()){var 
e=b.getNoSupportReason(),i="unsupported_dialog";e==="LOCALSTORAGE_DISABLED"&&(i="cookies_disabled"),g=window.open(c+"/"+i,null,f);return}k&&k.notify({method:"dialog_running"}),g=WinChan.open({url:c+"/sign_in",relay_url:c+"/relay",window_features:f,window_name:"__persona_dialog",params:{method:"get",params:a}},function(b,c){k&&(!b&&c&&c.email&&k.notify({method:"loggedInUser",params:c.email}),k.notify({method:"dialog_complete"})),g=undefined;if(!b&&c&&c.assertion)try{h.login&&h.login(c.assertion)}catch(d){}if(b==="client
 closed window"||!c)a&&a.oncancel&&a.oncancel(),delete 
a.oncancel})}}navigator.id={request:function(a){if(this!=navigator.id)throw new 
Error("all navigator.id calls must be made on the navigator.id 
object");a=a||{},j(!1),a.returnTo||(a.returnTo=document.location.pathname);return
 s(a)},watch:function(a){if(this!=navigator.id)throw new Error("all 
navigator.id calls must be made on the navigator.id 
object");j(!1),r(a)},logout:function(a){if(this!=navigator.id)throw new 
Error("all navigator.id calls must be made on the navigator.id 
object");m(),k&&k.notify({method:"logout"}),typeof 
a=="function"&&setTimeout(a,0)},get:function(a,b){var 
c={};b=b||{},c.privacyPolicy=b.privacyPolicy||undefined,c.termsOfService=b.termsOfService||undefined,c.privacyURL=b.privacyURL||undefined,c.tosURL=b.tosURL||undefined;p(b,"silent")?a&&setTimeout(function(){a(null)},0):(j(!0),r({onlogin:function(b){a&&(a(b),a=null)},onlogout:function(){}}),c.oncancel=function(){a&&(a(null),a=null),h.login=h.logout=h.ready=null},s(c))},getVerifiedEmail:function(a){o("navigator.id.getVerifiedEmail
 has been deprecated"),j(!0),navigator.id.get(a)},_shimmed:!0}}})()
\ No newline at end of file
diff --git a/js/persona_hooks.js b/js/persona_hooks.js
index 7b93338..1272c7f 100644
--- a/js/persona_hooks.js
+++ b/js/persona_hooks.js
@@ -1,57 +1,75 @@
-jQuery( function( $ ) {
-    $( '#wpPersona' ).click( function() {
-       navigator.id.request();
-    } );
+( function( $, mw ) {
+       'use strict';
 
-    $( '#pt-personalogin' ).click( function( event ) {
-       navigator.id.request();
-    } );
+       $( function( $ ) {
+               $( '#wpPersona' ).click( function() {
+                       navigator.id.request();
+               } );
 
-    $( '#pt-logout > a' ).click( function( event ) {
-       navigator.id.logout();
-    } );
+               $( '#pt-personalogin' ).click( function() {
+                       navigator.id.request();
+               } );
 
-    navigator.id.watch( {
-       loggedInUser: null,
-       onlogin: function( assertion ) {
-           var api = new mw.Api();
-           api.post( {
-               'action': 'persona',
-               'assertion': assertion,
-               'token': $( 'input[name="wpLoginToken"]' ).val(),
-               'stickhttps': $( '#wpStickHTTPS' ).val()
-           } )
-           .done( function ( data ) {
-               console.log( 'Persona login result:', data );
-               var vars = [], hash;
-               var q = document.URL.split( '?' )[1];
-               if( q != undefined ){
-                   q = q.split( '&' );
-                   for( var i = 0; i < q.length; i++ ){
-                       hash = q[i].split( '=' );
-                       vars.push( hash[1] );
-                       vars[hash[0]] = hash[1];
-                   }
-               } else {
-                       q = {};
-               }
+               $( '#pt-logout > a' ).click( function() {
+                       navigator.id.logout();
+               } );
 
-               var title;
-               if( q['returnto'] != undefined ) {
-                   title = new mw.Title( q['returnto'] );
-                       window.location.href = title.getUrl();
-               } else if( q['title'] != 'Special:Userlogin' ) {
-                       window.location.reload();
-               } else {
-                   title = new mw.Title( 'Main Page' );
-                       window.location.href = title.getUrl();
-               }
-           } )
-           .fail( function ( error ) {
-               console.log( 'Persona login failed.', error );
-               mw.util.jsMessage( 'Persona login failed.' );
-           } );
-       },
-       onlogout: function() {}
-    } );
-} );
+               navigator.id.watch( {
+                       loggedInUser: mw.config.get( 'wgPersonaUserEmail' ),
+                       onlogin: function( assertion ) {
+                               var api = new mw.Api();
+                               api.post( {
+                                       'action': 'persona',
+                                       'assertion': assertion,
+                                       'token': $( 
'input[name="wpLoginToken"]' ).val()
+                               } )
+                               .done( function ( data ) {
+                                       if ( data.login.status !== 'okay' ) {
+                                               console.log( 'Persona login 
failed.', data );
+                                               mw.notify( mw.message( 
'persona-error-' + data.login.status ).text() );
+                                               navigator.id.logout();
+                                               return;
+                                       }
+
+                                       var vars, url, queryPos, fragPos, hash, 
q, title, lowercaseTitle;
+
+                                       console.log( 'Persona login result:', 
data );
+                                       vars = [];
+                                       url = document.URL;
+                                       queryPos = url.indexOf( '?' ) + 1;
+                                       fragPos = url.indexOf( '#', queryPos );
+                                       q = document.URL.substring( queryPos, 
fragPos );
+                                       if ( queryPos < fragPos && q !== "" ){
+                                               q = q.split( '&' );
+                                               for ( var i = 0; i < q.length; 
i++ ) {
+                                                       hash = q[i].split( '=' 
);
+                                                       vars[hash[0]] = 
decodeURIComponent( hash[1] ).replace( '+', ' ' );
+                                               }
+                                       } else {
+                                               vars = {};
+                                       }
+
+                                       lowercaseTitle = vars.title !== 
undefined ? vars.title.toLowerCase() : undefined;
+                                       if ( vars.returnto !== undefined ) {
+                                               title = new mw.Title( 
vars.returnto );
+                                               window.location.href = 
title.getUrl();
+                                       } else if ( lowercaseTitle !== 
'special:userlogin' &&
+                                               lowercaseTitle !== 
'special:userlogout'
+                                       ) {
+                                               window.location.reload();
+                                       } else {
+                                               title = new mw.Title( 
'Main_Page' );
+                                               window.location.href = 
title.getUrl();
+                                       }
+                               } )
+                               .fail( function ( error ) {
+                                       console.log( 'Persona login failed.', 
error );
+                                       mw.notify( mw.message( error ).text() );
+                                       navigator.id.logout();
+                               } );
+                       },
+                       onlogout: function() {}
+               } );
+       } );
+
+} )( jQuery, mediaWiki );
diff --git a/js/persona_hooks_old.js b/js/persona_hooks_old.js
new file mode 100644
index 0000000..b0a09e2
--- /dev/null
+++ b/js/persona_hooks_old.js
@@ -0,0 +1,85 @@
+/*
+ * Old version of Persona hooks for compatibility with MediaWiki version
+ * 1.20 and possibly earlier. The main differences are:
+ *   * Old method of passing closures to mw.Api rather than using a promise
+ *   * Use of jsMessage rather than the newer mw.notify
+ */
+
+( function( $, mw ) {
+       'use strict';
+
+       $( function( $ ) {
+               $( '#wpPersona' ).click( function() {
+                       navigator.id.request();
+               } );
+
+               $( '#pt-personalogin' ).click( function() {
+                       navigator.id.request();
+               } );
+
+               $( '#pt-logout > a' ).click( function() {
+                       navigator.id.logout();
+               } );
+
+               navigator.id.watch( {
+                       loggedInUser: mw.config.get( 'wgPersonaUserEmail' ),
+                       onlogin: function( assertion ) {
+                               var api = new mw.Api();
+                               api.post(
+                                       {
+                                               'action': 'persona',
+                                               'assertion': assertion,
+                                               'token': $( 
'input[name="wpLoginToken"]' ).val()
+                                       },
+                                       {
+                                               'ok': function ( data ) {
+                                                       if ( data.login.status 
!== "okay" ) {
+                                                               console.log( 
'Persona login failed.', data );
+                                                               
mw.util.jsMessage( mw.msg( 'persona-error-' + data.login.status ) );
+                                                               
navigator.id.logout();
+                                                               return;
+                                                       }
+
+                                                       var vars, url, 
queryPos, fragPos, hash, q, title, lowercaseTitle;
+
+                                                       console.log( 'Persona 
login result:', data );
+                                                       vars = [];
+                                                       url = document.URL;
+                                                       queryPos = url.indexOf( 
'?' ) + 1;
+                                                       fragPos = url.indexOf( 
'#', queryPos );
+                                                       q = 
document.URL.substring( queryPos, fragPos );
+                                                       if ( queryPos < fragPos 
&& q !== "" ){
+                                                               q = q.split( 
'&' );
+                                                               for ( var i = 
0; i < q.length; i++ ) {
+                                                                       hash = 
q[i].split( '=' );
+                                                                       
vars[hash[0]] = decodeURIComponent( hash[1] ).replace( '+', ' ' );
+                                                               }
+                                                       } else {
+                                                               vars = {};
+                                                       }
+
+                                                       lowercaseTitle = 
vars.title !== undefined ? vars.title.toLowerCase() : undefined;
+                                                       if ( vars.returnto !== 
undefined ) {
+                                                               title = new 
mw.Title( vars.returnto );
+                                                               
window.location.href = title.getUrl();
+                                                       } else if ( 
lowercaseTitle !== 'special:userlogin' &&
+                                                               lowercaseTitle 
!== 'special:userlogout'
+                                                       ) {
+                                                               
window.location.reload();
+                                                       } else {
+                                                               title = new 
mw.Title( 'Main_Page' );
+                                                               
window.location.href = title.getUrl();
+                                                       }
+                                               },
+                                               'err': function ( error ) {
+                                                       console.log( 'Persona 
login failed.', error );
+                                                       mw.util.jsMessage( 
mw.msg( error ) );
+                                                       navigator.id.logout();
+                                               }
+                                       }
+                               );
+                       },
+                       onlogout: function() {}
+               } );
+       } );
+} )( jQuery, mediaWiki );
diff --git a/persona.crt b/persona.crt
index ed0bd76..4137243 100644
--- a/persona.crt
+++ b/persona.crt
@@ -1,19 +1,21 @@
 -----BEGIN CERTIFICATE-----
-MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
-UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
-dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
-MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
-dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
-AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
-BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
-cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
-AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
-MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
-aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
-ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
-IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
-MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
-A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
-7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
-1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+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
 -----END CERTIFICATE-----

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

Gerrit-MessageType: merged
Gerrit-Change-Id: If61cf139256e411440eb5a327f32f37bd6dfb962
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/Persona
Gerrit-Branch: master
Gerrit-Owner: Parent5446 <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: Parent5446 <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: jenkins-bot

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

Reply via email to