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

Change subject: Refactored special pages into HTMLForm and proxy
......................................................................


Refactored special pages into HTMLForm and proxy

Made new class ProxySpecialPage, which acts as a
proxy object to another SpecialPage object that is
determined based on context information other than
the title.

Then Special:OATH has been split into two separate
special page classes (both FormSpecialPages using
HTMLForm) that are routed to by a ProxySpecialPage
object.

In addition, the form for enabling two-factor auth
has been refactored into vform style, with some
better instructions on how to enable two-factor
authentication.

Change-Id: Ib9117cbc9d7f044de9607db81a157e1b472b5ec0
(cherry picked from commit 0c389f50255325338a03fed8739a923ad2aefc1e)
---
M OATHAuth.hooks.php
M OATHAuthKey.php
M OATHUser.php
M extension.json
M i18n/en.json
M i18n/qqq.json
A special/ProxySpecialPage.php
M special/SpecialOATH.php
A special/SpecialOATHDisable.php
A special/SpecialOATHEnable.php
10 files changed, 575 insertions(+), 279 deletions(-)

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



diff --git a/OATHAuth.hooks.php b/OATHAuth.hooks.php
index 8bbc24d..2df0313 100644
--- a/OATHAuth.hooks.php
+++ b/OATHAuth.hooks.php
@@ -2,6 +2,8 @@
 
 /**
  * Hooks for Extension:OATHAuth
+ *
+ * @ingroup Extensions
  */
 class OATHAuthHooks {
        /**
@@ -128,39 +130,20 @@
                $oathUser = $oathrepo->findByUser( $user );
 
                $title = SpecialPage::getTitleFor( 'OATH' );
-               if ( $oathUser->getKey() !== null ) {
-                       $preferences['oath-disable'] = array(
-                               'type' => 'info',
-                               'raw' => 'true',
-                               'default' => Linker::link(
-                                       $title,
-                                       wfMessage( 'oathauth-disable' 
)->escaped(),
-                                       array(),
-                                       array(
-                                               'action' => 'disable',
-                                               'returnto' => 
SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
-                                       )
-                               ),
-                               'label-message' => 'oathauth-prefs-label',
-                               'section' => 'personal/info',
-                       );
-               } else {
-                       $preferences['oath-enable'] = array(
-                               'type' => 'info',
-                               'raw' => 'true',
-                               'default' => Linker::link(
-                                       $title,
-                                       wfMessage( 'oathauth-enable' 
)->escaped(),
-                                       array(),
-                                       array(
-                                               'action' => 'enable',
-                                               'returnto' => 
SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
-                                       )
-                               ),
-                               'label-message' => 'oathauth-prefs-label',
-                               'section' => 'personal/info',
-                       );
-               }
+               $msg = $oathUser->getKey() !== null ? 'oathauth-disable' : 
'oathauth-enable';
+
+               $preferences[$msg] = array(
+                       'type' => 'info',
+                       'raw' => 'true',
+                       'default' => Linker::link(
+                               $title,
+                               wfMessage( $msg )->escaped(),
+                               array(),
+                               array( 'returnto' => SpecialPage::getTitleFor( 
'Preferences' )->getPrefixedText() )
+                       ),
+                       'label-message' => 'oathauth-prefs-label',
+                       'section' => 'personal/info',
+               );
 
                return true;
        }
diff --git a/OATHAuthKey.php b/OATHAuthKey.php
index e50826d..ac4a26b 100644
--- a/OATHAuthKey.php
+++ b/OATHAuthKey.php
@@ -4,8 +4,22 @@
  * Class representing a two-factor key
  *
  * Keys can be tied to OAUTHUsers
+ *
+ * @ingroup Extensions
  */
 class OATHAuthKey {
+       /**
+        * Represents that a token corresponds to the main secret
+        * @see verifyToken
+        */
+       const MAIN_TOKEN = 1;
+
+       /**
+        * Represents that a token corresponds to a scratch token
+        * @see verifyToken
+        */
+       const SCRATCH_TOKEN = -1;
+
        /** @var string Two factor binary secret */
        private $secret;
 
@@ -63,7 +77,8 @@
         * @param string $token Token to verify
         * @param OATHUser $user
         *
-        * @return bool True on match, false otherwise
+        * @return int|false Returns a constant represent what type of token 
was matched,
+        *  or false for no match
         */
        public function verifyToken( $token, $user ) {
                global $wgOATHAuthWindowRadius;
@@ -86,7 +101,7 @@
                foreach ( $results as $window => $result ) {
                        if ( $window > $lastWindow && $result->toHOTP( 6 ) === 
$token ) {
                                $lastWindow = $window;
-                               $retval = true;
+                               $retval = self::MAIN_TOKEN;
                                break;
                        }
                }
@@ -106,7 +121,7 @@
                                                $user->setKey( $this );
                                                $oathrepo->persist( $user );
                                                // Only return true if we 
removed it from the database
-                                               $retval = true;
+                                               $retval = self::SCRATCH_TOKEN;
                                                break;
                                        }
                                }
diff --git a/OATHUser.php b/OATHUser.php
index 7f19d0d..64efd33 100644
--- a/OATHUser.php
+++ b/OATHUser.php
@@ -3,7 +3,6 @@
 /**
  * Class representing a user from OATH's perspective
  *
- * @file
  * @ingroup Extensions
  */
 class OATHUser {
diff --git a/extension.json b/extension.json
index ae0677d..9a47fa4 100644
--- a/extension.json
+++ b/extension.json
@@ -13,7 +13,10 @@
                "HOTPResult": "lib/hotp.php",
                "Base32": "lib/base32.php",
                "OATHUser": "OATHUser.php",
-               "SpecialOATH": "special/SpecialOATH.php"
+               "SpecialOATH": "special/SpecialOATH.php",
+               "SpecialOATHEnable": "special/SpecialOATHEnable.php",
+               "SpecialOATHDisable": "special/SpecialOATHDisable.php",
+               "ProxySpecialPage": "special/ProxySpecialPage.php"
        },
        "ExtensionMessagesFiles": {
                "OATHAuthAlias": "OATHAuth.alias.php"
diff --git a/i18n/en.json b/i18n/en.json
index 54b89b2..66568c1 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -1,13 +1,15 @@
 {
        "@metadata": {
                "authors": [
-                       "Ryan Lane <rl...@wikimedia.org>"
+                       "Ryan Lane <rl...@wikimedia.org>",
+                       "Tyler Romeo <tylerro...@gmail.com>"
                ]
        },
        "oathauth-desc": "Provides authentication support using HMAC based 
one-time passwords",
        "oath": "OATHAuth",
        "specialpages-group-oath": "Two-factor authentication",
        "oathauth-account": "Two-factor account name:",
+       "oathauth-legend": "Verify your credentials",
        "oathauth-secret": "Two-factor secret key:",
        "oathauth-enable": "Enable two-factor authentication",
        "oathauth-failedtoenableoauth": "Failed to enable two-factor 
authentication.",
@@ -33,5 +35,12 @@
        "oathauth-notloggedin": "Login required",
        "oathauth-mustbeloggedin": "You must be logged in to perform this 
action.",
        "oathauth-prefs-label": "Two-factor authentication:",
-       "oathauth-abortlogin": "The two-factor authentication token provided 
was invalid."
+       "oathauth-abortlogin": "The two-factor authentication token provided 
was invalid.",
+       "oathauth-step1": "Step 1: Download the app",
+       "oathauth-step1-test": "Download a mobile app for two-factor 
authentication (such as Google Authenticator) on to your phone.",
+       "oathauth-step2": "Step 2: Scan the QR code",
+       "oathauth-step2alt": "Or enter the secret manually:",
+       "oathauth-step3": "Step 3: Write down the scratch codes",
+       "oathauth-step4": "Step 4: Verification",
+       "oathauth-entertoken": "Enter a code from your mobile app to verify:"
 }
diff --git a/i18n/qqq.json b/i18n/qqq.json
index a45e378..369a1e3 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -5,13 +5,15 @@
                        "Ryan Lane <rl...@wikimedia.org>",
                        "Shirayuki",
                        "Purodha",
-                       "Umherirrender"
+                       "Umherirrender",
+            "Tyler Romeo <tylerro...@gmail.com>"
                ]
        },
        "oathauth-desc": "{{desc|name=OATH 
Auth|url=https://www.mediawiki.org/wiki/Extension:OATHAuth}}";,
        "oath": "{{optional}}\n{{doc-special|OATH}}",
        "specialpages-group-oath": 
"{{doc-special-group|like=[[Special:OATH]]}}\n\nSee 
[[w:en:Two_factor_authentication|Wikipedia article on two factor 
authentication]].\n\n{{Identical|Two factor authentication}}",
        "oathauth-account": "Plain text associated with 
[https://en.wikipedia.org/wiki/Two_factor_authentication two factor 
authentication] on this wiki (username@<wiki name>) found on Special:OATH.",
+       "oathauth-legend": "Label for the legend on Special:OATH",
        "oathauth-secret": "Plain text found on Special:OATH while enabling 
OATH\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor 
authentication]",
        "oathauth-enable": "Page title on Special:OATH, when enabling 
OATH.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two 
factor authentication]",
        "oathauth-failedtoenableoauth": "Plain text, found on Special:OATH when 
failing to enable OATH.\n\nSee 
[https://en.wikipedia.org/wiki/Two_factor_authentication two factor 
authentication]",
@@ -37,5 +39,12 @@
        "oathauth-notloggedin": "Page title seen on Special:OATH when a user is 
not logged in.\n{{Identical|Login required}}",
        "oathauth-mustbeloggedin": "Plain text seen on Special:OATH when a user 
is not logged in.",
        "oathauth-prefs-label": "Plain text label seen on 
Special:Preferences\n\nSee 
[https://en.wikipedia.org/wiki/Two_factor_authentication two factor 
authentication]",
-       "oathauth-abortlogin": "Error message shown on login and password 
change pages when authentication is aborted.\n\nSee 
[https://en.wikipedia.org/wiki/Two_factor_authentication two factor 
authentication]"
+       "oathauth-abortlogin": "Error message shown on login and password 
change pages when authentication is aborted.\n\nSee 
[https://en.wikipedia.org/wiki/Two_factor_authentication two factor 
authentication]",
+    "oathauth-step1": "Label for step 1 on Special:OATH form",
+    "oathauth-step1-test": "Text for step 1 on Special:OATH form",
+    "oathauth-step2": "Label for step 2, the QR code, on Special:OATH",
+    "oathauth-step2alt": "Label for information on how to manually do step 2 
on Special:OATH",
+    "oathauth-step3": "Label for step 3 information on Special:OATH",
+    "oathauth-step4": "Label for step 4 information on Special:OATH",
+    "oathauth-entertoken": "Label on input field on Special:OATH asking user 
to enter token"
 }
diff --git a/special/ProxySpecialPage.php b/special/ProxySpecialPage.php
new file mode 100644
index 0000000..23e84de
--- /dev/null
+++ b/special/ProxySpecialPage.php
@@ -0,0 +1,212 @@
+<?php
+
+/**
+ * A proxy class that routes a special page to other special pages based on
+ * request parameters
+ */
+abstract class ProxySpecialPage extends SpecialPage {
+       /**
+        * @var SpecialPage|null Target page to execute
+        */
+       private $target = null;
+
+       /**
+        * Instantiate a SpecialPage based on request parameters
+        *
+        * The page returned by this function will be cached and used as
+        * the target page for this proxy object.
+        *
+        * @return SpecialPage
+        */
+       abstract protected function getTargetPage();
+
+       /**
+        * Helper function that initializes the target SpecialPage object
+        */
+       private function init() {
+               if ( $this->target === null ) {
+                       $this->target = $this->getTargetPage();
+               }
+       }
+
+       /**
+        * Magic function that proxies function calls to the target object
+        *
+        * @param string $method Method name being called
+        * @param array $args Array of arguments
+        *
+        * @return mixed
+        */
+       public function __call( $method, $args ) {
+               $this->init();
+               return call_user_func_array( array( $this->target, $method ), 
$args );
+       }
+
+
+       /**
+        * @return string
+        */
+       function getName() {
+               $this->init();
+               return $this->target->getName();
+       }
+
+       /**
+        * @param string|bool $subpage
+        * @return Title
+        */
+       function getPageTitle( $subpage = false ) {
+               $this->init();
+               return $this->target->getPageTitle( $subpage );
+       }
+
+       /**
+        * @return string
+        */
+       function getLocalName() {
+               $this->init();
+               return $this->target->getLocalName();
+       }
+
+       /**
+        * @return string
+        */
+       function getRestriction() {
+               $this->init();
+               return $this->target->getRestriction();
+       }
+
+       /**
+        * @return bool
+        */
+       function isListed() {
+               $this->init();
+               return $this->target->isListed();
+       }
+
+       /**
+        * @param bool $listed
+        * @return bool
+        */
+       function setListed( $listed ) {
+               $this->init();
+               return $this->target->setListed( $listed );
+       }
+
+       /**
+        * @param bool $x
+        * @return bool
+        */
+       function listed( $x = null ) {
+               $this->init();
+               return $this->target->listed( $x );
+       }
+
+       /**
+        * @return bool
+        */
+       public function isIncludable() {
+               $this->init();
+               return $this->target->isIncludable();
+       }
+
+       /**
+        * @param bool $x
+        * @return bool
+        */
+       function including( $x = null ) {
+               $this->init();
+               return $this->target->including( $x );
+       }
+
+       /**
+        * @return bool
+        */
+       public function isRestricted() {
+               $this->init();
+               return $this->target->isRestricted();
+       }
+
+       /**
+        * @param User $user
+        * @return bool
+        */
+       public function userCanExecute( User $user ) {
+               $this->init();
+               return $this->target->userCanExecute( $user );
+       }
+
+       /**
+        * @throws PermissionsError
+        */
+       function displayRestrictionError() {
+               $this->init();
+               $this->target->displayRestrictionError();
+       }
+
+       /**
+        * @return void
+        * @throws PermissionsError
+        */
+       public function checkPermissions() {
+               $this->init();
+               $this->target->checkPermissions();
+       }
+
+       /**
+        * @param string|null $subPage
+        */
+       protected function beforeExecute( $subPage ) {
+               $this->init();
+               $this->target->beforeExecute( $subPage );
+       }
+
+       /**
+        * @param string|null $subPage
+        */
+       protected function afterExecute( $subPage ) {
+               $this->init();
+               $this->target->afterExecute( $subPage );
+       }
+
+       /**
+        * @param string|null $subPage
+        */
+       public function execute( $subPage ) {
+               $this->init();
+               $this->target->execute( $subPage );
+       }
+
+       /**
+        * @return string
+        */
+       function getDescription() {
+               $this->init();
+               return $this->target->getDescription();
+       }
+
+       /**
+        * @param IContextSource $context
+        */
+       public function setContext( $context ) {
+               $this->init();
+               $this->target->setContext( $context );
+               parent::setContext( $context );
+       }
+
+       /**
+        * @return string
+        */
+       protected function getRobotPolicy() {
+               $this->init();
+               return $this->target->getRobotPolicy();
+       }
+
+       /**
+        * @return string
+        */
+       protected function getGroupName() {
+               $this->init();
+               return $this->target->getGroupName();
+       }
+}
diff --git a/special/SpecialOATH.php b/special/SpecialOATH.php
index a4c826e..5ab08dd 100644
--- a/special/SpecialOATH.php
+++ b/special/SpecialOATH.php
@@ -1,250 +1,24 @@
 <?php
 
 /**
- * Special page to display key information to the user
- *
- * @file
- * @ingroup Extensions
+ * Proxy page that redirects to the proper OATH special page
  */
-
-class SpecialOATH extends UnlistedSpecialPage {
-       /** @var OATHUser|null */
-       private $OATHUser;
-
+class SpecialOATH extends ProxySpecialPage {
        /**
-        * Initialize the OATH user based on the current local User object in 
the context
-        */
-       public function __construct() {
-               parent::__construct( 'OATH' );
-
-               $this->OATHRepository = new OATHUserRepository( wfGetLB() );
-               $this->OATHUser = $this->OATHRepository->findByUser( 
$this->getUser() );
-       }
-
-       /**
-        * Perform the correct form based on the action
+        * If the user already has OATH enabled, show them a page to disable
+        * If the user has OATH disabled, show them a page to enable
         *
-        * @param null|string $par Sub-page
+        * @return SpecialOATHDisable|SpecialOATHEnable|SpecialPage
         */
-       public function execute( $par ) {
-               if ( !$this->getUser()->isLoggedIn() ) {
-                       $this->setHeaders();
-                       $this->getOutput()->setPagetitle( $this->msg( 
'oathauth-notloggedin' ) );
-                       $this->getOutput()->addWikiMsg( 
'oathauth-mustbeloggedin' );
-                       return;
-               }
+       protected function getTargetPage() {
+               $repo = new OATHUserRepository( wfGetLB() );
+               $user = $repo->findByUser( $this->getUser() );
 
-               $action = $this->getRequest()->getVal( 'action' );
-               if ( $action == "enable" ) {
-                       $this->enable();
-               } elseif ( $action == "disable" ) {
-                       $this->disable();
-               }
-       }
-
-       /**
-        * @return bool
-        */
-       private function enable() {
-               $this->setHeaders();
-               $this->getOutput()->setPagetitle( $this->msg( 'oathauth-enable' 
) );
-               $returnto = $this->getRequest()->getVal( 'returnto' );
-
-               if ( $this->OATHUser->getKey() ) {
-                       $this->getOutput()->addWikiMsg( 
'oathauth-alreadyenabled' );
-
-                       return true;
-               }
-
-               if ( null === $this->getRequest()->getSessionData( 
'oathauth_key' ) ) {
-                       $this->getRequest()->setSessionData( 'oathauth_key', 
OATHAuthKey::newFromRandom() );
-               }
-
-               $info['token'] = array(
-                       'type' => 'text',
-                       'default' => '',
-                       'label-message' => 'oathauth-token',
-                       'name' => 'token',
-               );
-               $info['mode'] = array(
-                       'type' => 'hidden',
-                       'default' => 'enable',
-                       'name' => 'mode',
-               );
-               $info['returnto'] = array(
-                       'type' => 'hidden',
-                       'default' => $returnto,
-                       'name' => 'returnto',
-               );
-               $info['action'] = array(
-                       'type' => 'hidden',
-                       'default' => 'enable',
-                       'name' => 'action',
-               );
-               $form = new HTMLForm(
-                       $info,
-                       $this->getContext(),
-                       'oathauth-verify'
-               );
-               $form->setSubmitID( 'oathauth-validate-submit' );
-               $form->setSubmitCallback( array( $this, 'tryValidateSubmit' ) );
-               if ( !$form->show() ) {
-                       $this->displaySecret();
-               }
-
-               return true;
-       }
-
-       private function displaySecret() {
-               $this->getOutput()->addModules( 'ext.oathauth' );
-
-               /** @var OATHAuthKey $key */
-               $key = $this->getRequest()->getSessionData( 'oathauth_key' );
-               $secret = $key->getSecret();
-
-               $out = '<strong>' . $this->msg( 'oathauth-account' )->escaped() 
. '</strong> '
-                       . $this->OATHUser->getAccount() . '<br/>'
-                       . '<strong>' . $this->msg( 'oathauth-secret' 
)->escaped() . '</strong> '
-                       . $secret . '<br/>'
-                       . '<br/>'
-                       . '<div id="qrcode"></div>';
-
-               $this->getOutput()->addHTML( ResourceLoader::makeInlineScript(
-                       Xml::encodeJsCall( 'mw.loader.using', array(
-                               array( 'ext.oathauth' ),
-                               new XmlJsCode(
-                                       'function () {'
-                                               . 
'$("#qrcode").qrcode("otpauth://totp/'
-                                               . $this->OATHUser->getAccount()
-                                               . '?secret=' . $secret. '");'
-                                       . '}'
-                               )
-                       ) )
-               ) );
-
-               $this->getOutput()->addHTML( $out );
-               $this->getOutput()->addWikiMsg( 
'openstackmanager-scratchtokens' );
-               $this->getOutput()->addHTML(
-                       $this->createResourceList( $key->getScratchTokens() ) );
-       }
-
-       /**
-        * @return bool
-        */
-       private function disable() {
-               $this->setHeaders();
-               $this->getOutput()->setPagetitle( $this->msg( 
'oathauth-disable' ) );
-               $returnto = $this->getRequest()->getVal( 'returnto' );
-
-               $info['token'] = array(
-                       'type' => 'text',
-                       'label-message' => 'oathauth-token',
-                       'name' => 'token',
-               );
-               $info['returnto'] = array(
-                       'type' => 'hidden',
-                       'default' => $returnto,
-                       'name' => 'returnto',
-               );
-               $info['action'] = array(
-                       'type' => 'hidden',
-                       'default' => 'disable',
-                       'name' => 'action',
-               );
-               $form = new HTMLForm(
-                       $info,
-                       $this->getContext(),
-                       'oathauth-disable'
-               );
-               $form->setSubmitID( 'oauth-form-disablesubmit' );
-               $form->setSubmitCallback( array( $this, 'tryDisableSubmit' ) );
-               $form->show();
-               return true;
-       }
-
-       /**
-        * @param $resources array
-        * @return string
-        */
-       private function createResourceList( $resources ) {
-               $resourceList = '';
-               foreach ( $resources as $resource ) {
-                       $resourceList .= Html::rawElement( 'li', array(), 
$resource );
-               }
-               return Html::rawElement( 'ul', array(), $resourceList );
-       }
-
-       /**
-        * @param $formData array
-        * @return bool
-        */
-       public function tryValidateSubmit( $formData ) {
-               /** @var OATHAuthKey $key */
-               $key = $this->getRequest()->getSessionData( 'oathauth_key' );
-
-               $verify = $key->verifyToken( $formData['token'], 
$this->OATHUser );
-               $out = '';
-               if ( $verify ) {
-                       $this->OATHUser->setKey( $key );
-                       $this->OATHRepository->persist( $this->OATHUser );
-                       $this->getRequest()->setSessionData( 'oathauth_key', 
null );
-
-                       $this->getOutput()->addWikiMsg( 
'oathauth-validatedoath' );
-                       if ( $formData['returnto'] ) {
-                               $out = '<br />';
-                               $title = Title::newFromText( 
$formData['returnto'] );
-                               $out .= Linker::link( $title, $this->msg( 
'oathauth-backtopreferences' )->escaped() );
-                       }
+               if ( $user->getKey() === null ) {
+                       return new SpecialOATHEnable( $repo, $user );
                } else {
-                       $this->getOutput()->addWikiMsg( 
'oathauth-failedtovalidateoauth' );
-                       $out = '<br />';
-
-                       $out .= Linker::link(
-                               $this->getPageTitle(),
-                               $this->msg( 'oathauth-reattemptenable' 
)->escaped(),
-                               array(),
-                               array(
-                                       'action' => 'enable',
-                                       'returnto' => $formData['returnto']
-                               )
-                       );
+                       return new SpecialOATHDisable( $repo, $user );
                }
-
-               $this->getOutput()->addHTML( $out );
-
-               return true;
-       }
-
-       /**
-        * @param $formData array
-        * @return bool
-        */
-       public function tryDisableSubmit( $formData ) {
-               $verify = $this->OATHUser->getKey()->verifyToken( 
$formData['token'], $this->OATHUser );
-               if ( !$verify ) {
-                       $this->getOutput()->addWikiMsg( 
'oathauth-failedtovalidateoauth' );
-                       $out = '<br />';
-                       $out .= Linker::link(
-                               $this->getPageTitle(),
-                               $this->msg( 'oathauth-reattemptdisable' 
)->escaped(),
-                               array(),
-                               array( 'action' => 'disable' )
-                       );
-                       $this->getOutput()->addHTML( $out );
-                       return true;
-               }
-
-               $this->OATHRepository->remove( $this->OATHUser );
-
-               $this->getOutput()->addWikiMsg( 'oathauth-disabledoath' );
-               if ( $formData['returnto'] ) {
-                       $out = '<br />';
-                       $title = Title::newFromText( $formData['returnto'] );
-                       $out .= Linker::link( $title, $this->msg( 
'oathauth-backtopreferences' )->escaped() );
-                       $this->getOutput()->addHTML( $out );
-               }
-
-               return true;
        }
 
        protected function getGroupName() {
diff --git a/special/SpecialOATHDisable.php b/special/SpecialOATHDisable.php
new file mode 100644
index 0000000..a0db4ae
--- /dev/null
+++ b/special/SpecialOATHDisable.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * Special page to display key information to the user
+ *
+ * @file
+ * @ingroup Extensions
+ */
+class SpecialOATHDisable extends FormSpecialPage {
+       /** @var OATHUserRepository */
+       private $OATHRepository;
+
+       /** @var OATHUser */
+       private $OATHUser;
+
+       /**
+        * Initialize the OATH user based on the current local User object in 
the context
+        *
+        * @param OATHUserRepository $repository
+        * @param OATHUser $user
+        */
+       public function __construct( OATHUserRepository $repository, OATHUser 
$user ) {
+               parent::__construct( 'OATH', '', false );
+               $this->OATHRepository = $repository;
+               $this->OATHUser = $user;
+       }
+
+       /**
+        * Set the page title and add JavaScript RL modules
+        *
+        * @param HTMLForm $form
+        */
+       public function alterForm( HTMLForm $form ) {
+               $form->setMessagePrefix( 'oathauth' );
+               $form->setWrapperLegend( false );
+               $form->getOutput()->setPagetitle( $this->msg( 
'oathauth-disable' ) );
+               $form->getOutput()->addModules( 'ext.oathauth' );
+       }
+
+       /**
+        * @return string
+        */
+       protected function getDisplayFormat() {
+               return 'vform';
+       }
+
+       /**
+        * @return bool
+        */
+       public function requiresUnblock() {
+               return false;
+       }
+
+       /**
+        * Require users to be logged in
+        *
+        * @param User $user
+        *
+        * @return bool|void
+        */
+       protected function checkExecutePermissions( User $user ) {
+               parent::checkExecutePermissions( $user );
+
+               $this->requireLogin();
+       }
+
+       /**
+        * @return array[]
+        */
+       protected function getFormFields() {
+               return array(
+                       'token' => array(
+                               'type' => 'text',
+                               'label-message' => 'oathauth-entertoken',
+                               'name' => 'token',
+                       ),
+                       'returnto' => array(
+                               'type' => 'hidden',
+                               'default' => $this->getRequest()->getVal( 
'returnto' ),
+                               'name' => 'returnto',
+                       ),
+                       'returntoquery' => array(
+                               'type' => 'hidden',
+                               'default' => $this->getRequest()->getVal( 
'returntoquery' ),
+                               'name' => 'returntoquery',
+                       )
+               );
+       }
+
+       /**
+        * @param array $formData
+        *
+        * @return array|bool
+        */
+       public function onSubmit( array $formData ) {
+               if ( !$this->OATHUser->getKey()->verifyToken( 
$formData['token'], $this->OATHUser ) ) {
+                       return array( 'oathauth-failedtovalidateoauth' );
+               }
+
+               $this->OATHUser->setKey( null );
+               $this->OATHRepository->remove( $this->OATHUser );
+
+               return true;
+       }
+
+       public function onSuccess() {
+               $this->getOutput()->addWikiMsg( 'oathauth-disabledoath' );
+               $this->getOutput()->returnToMain();
+       }
+}
diff --git a/special/SpecialOATHEnable.php b/special/SpecialOATHEnable.php
new file mode 100644
index 0000000..1fb4bc4
--- /dev/null
+++ b/special/SpecialOATHEnable.php
@@ -0,0 +1,182 @@
+<?php
+
+/**
+ * Special page to display key information to the user
+ *
+ * @file
+ * @ingroup Extensions
+ */
+class SpecialOATHEnable extends FormSpecialPage {
+       /** @var OATHUserRepository */
+       private $OATHRepository;
+
+       /** @var OATHUser */
+       private $OATHUser;
+
+       /**
+        * Initialize the OATH user based on the current local User object in 
the context
+        *
+        * @param OATHUserRepository $repository
+        * @param OATHUser $user
+        */
+       public function __construct( OATHUserRepository $repository, OATHUser 
$user ) {
+               parent::__construct( 'OATH', '', false );
+
+               $this->OATHRepository = $repository;
+               $this->OATHUser = $user;
+       }
+
+       /**
+        * Set the page title and add JavaScript RL modules
+        *
+        * @param HTMLForm $form
+        */
+       public function alterForm( HTMLForm $form ) {
+               $form->setMessagePrefix( 'oathauth' );
+               $form->setWrapperLegend( false );
+               $form->getOutput()->setPagetitle( $this->msg( 'oathauth-enable' 
) );
+               $form->getOutput()->addModules( 'ext.oathauth' );
+       }
+
+       /**
+        * @return string
+        */
+       protected function getDisplayFormat() {
+               return 'vform';
+       }
+
+       /**
+        * @return bool
+        */
+       public function requiresUnblock() {
+               return false;
+       }
+
+       /**
+        * Require users to be logged in
+        *
+        * @param User $user
+        *
+        * @return bool|void
+        */
+       protected function checkExecutePermissions( User $user ) {
+               parent::checkExecutePermissions( $user );
+
+               $this->requireLogin();
+       }
+
+       /**
+        * @return array[]
+        */
+       protected function getFormFields() {
+               $key = $this->getRequest()->getSessionData( 'oathauth_key' );
+
+               if ( $key === null ) {
+                       $key = OATHAuthKey::newFromRandom();
+                       $this->getRequest()->setSessionData( 'oathauth_key', 
$key );
+               }
+
+               $secret = $key->getSecret();
+
+               $this->getOutput()->addHTML( ResourceLoader::makeInlineScript(
+                       Xml::encodeJsCall( 'mw.loader.using', array(
+                               array( 'ext.oathauth' ),
+                               new XmlJsCode(
+                                       'function () {'
+                    . '$("#qrcode").qrcode("otpauth://totp/'
+                    . $this->OATHUser->getAccount()
+                    . '?secret=' . $secret. '");'
+                                       . '}'
+                               )
+                       ) )
+               ) );
+
+               return array(
+                       'app' => array(
+                               'type' => 'info',
+                               'default' => $this->msg( 'oathauth-step1-test' 
)->escaped(),
+                               'raw' => true,
+                               'section' => 'step1',
+                       ),
+                       'qrcode' => array(
+                               'type' => 'info',
+                               'default' => '<div id="qrcode"></div>',
+                               'raw' => true,
+                               'section' => 'step2',
+                       ),
+                       'manual' => array(
+                               'type' => 'info',
+                               'label-message' => 'oathauth-step2alt',
+                               'default' =>
+                                       '<strong>' . $this->msg( 
'oathauth-account' )->escaped() . '</strong><br/>'
+                                       . $this->OATHUser->getAccount() . 
'<br/><br/>'
+                                       . '<strong>' . $this->msg( 
'oathauth-secret' )->escaped() . '</strong><br/>'
+                                       . $key->getSecret() . '<br/>',
+                               'raw' => true,
+                               'section' => 'step2',
+                       ),
+                       'scratchtokens' => array(
+                               'type' => 'info',
+                               'default' =>
+                                       $this->msg( 
'openstackmanager-scratchtokens' )
+                                       . $this->createResourceList( 
$key->getScratchTokens() ),
+                               'raw' => true,
+                               'section' => 'step3',
+                       ),
+                       'token' => array(
+                               'type' => 'text',
+                               'default' => '',
+                               'label-message' => 'oathauth-entertoken',
+                               'name' => 'token',
+                               'section' => 'step4',
+                       ),
+                       'returnto' => array(
+                               'type' => 'hidden',
+                               'default' => $this->getRequest()->getVal( 
'returnto' ),
+                               'name' => 'returnto',
+                       ),
+                       'returntoquery' => array(
+                               'type' => 'hidden',
+                               'default' => $this->getRequest()->getVal( 
'returntoquery' ),
+                               'name' => 'returntoquery',
+                       )
+               );
+       }
+
+       /**
+        * @param array $formData
+        *
+        * @return array|bool
+        */
+       public function onSubmit( array $formData ) {
+               /** @var OATHAuthKey $key */
+               $key = $this->getRequest()->getSessionData( 'oathauth_key' );
+
+               if ( !$key->verifyToken( $formData['token'], $this->OATHUser ) 
) {
+                       return array( 'oathauth-failedtovalidateoauth' );
+               }
+
+               $this->getRequest()->setSessionData( 'oathauth_key', null );
+               $this->OATHUser->setKey( $key );
+               $this->OATHRepository->persist( $this->OATHUser );
+
+               return true;
+       }
+
+       public function onSuccess() {
+               $this->getOutput()->addWikiMsg( 'oathauth-validatedoath' );
+               $this->getOutput()->returnToMain();
+       }
+
+       /**
+        * @param $resources array
+        * @return string
+        */
+       private function createResourceList( $resources ) {
+               $resourceList = '';
+               foreach ( $resources as $resource ) {
+                       $resourceList .= Html::rawElement( 'li', array(), 
$resource );
+               }
+               return Html::rawElement( 'ul', array(), $resourceList );
+       }
+}

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Ib9117cbc9d7f044de9607db81a157e1b472b5ec0
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/OATHAuth
Gerrit-Branch: wmf/1.27.0-wmf.18
Gerrit-Owner: CSteipp <cste...@wikimedia.org>
Gerrit-Reviewer: CSteipp <cste...@wikimedia.org>
Gerrit-Reviewer: Parent5446 <tylerro...@gmail.com>
Gerrit-Reviewer: Siebrand <siebr...@kitano.nl>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to