Merge "Move token login to separate page"

This commit is contained in:
jenkins-bot 2016-03-31 04:31:39 +00:00 committed by Gerrit Code Review
commit 1dd09985d0
8 changed files with 175 additions and 79 deletions

View file

@ -6,23 +6,6 @@
* @ingroup Extensions
*/
class OATHAuthHooks {
/**
* @param $template UserloginTemplate
* @return bool
*/
static function ModifyUITemplate( &$template ) {
$input = '<div><label for="wpOATHToken">'
. wfMessage( 'oathauth-token' )->escaped()
. '</label>'
. Html::input( 'wpOATHToken', null, 'text', array(
'class' => 'loginText', 'id' => 'wpOATHToken', 'tabindex' => '3', 'size' => '20'
) ) . '</div>';
$template->set( 'extrafields', $template->get( 'extrafields', '' ) . $input );
return true;
}
/**
* Get the singleton OATH user repository
*
@ -59,7 +42,19 @@ class OATHAuthHooks {
* @return bool
*/
static function AbortChangePassword( $user, $password, $newpassword, &$errorMsg ) {
$result = self::authenticate( $user );
global $wgRequest;
$token = $wgRequest->getText( 'wpOATHToken' );
$oathrepo = self::getOATHUserRepository();
$oathuser = $oathrepo->findByUser( $user );
# Though it's weird to default to true, we only want to deny
# users who have two-factor enabled and have validated their
# token.
$result = true;
if ( $oathuser->getKey() !== null ) {
$result = $oathuser->getKey()->verifyToken( $token, $oathuser );
}
if ( $result ) {
return true;
@ -78,57 +73,18 @@ class OATHAuthHooks {
* @return bool
*/
static function AbortLogin( $user, $password, &$abort, &$errorMsg ) {
$result = self::authenticate( $user );
if ( $result ) {
return true;
} else {
$abort = LoginForm::ABORTED;
$errorMsg = 'oathauth-abortlogin';
return false;
}
}
$context = RequestContext::getMain();
$request = $context->getRequest();
$output = $context->getOutput();
/**
* @param $user User
* @return bool
*/
static function authenticate( $user ) {
global $wgRequest;
$oathrepo = self::getOATHUserRepository();
$oathuser = $oathrepo->findByUser( $user );
$token = $wgRequest->getText( 'wpOATHToken' );
$oathuser = self::getOATHUserRepository()->findByUser( $user );
# Though it's weird to default to true, we only want to deny
# users who have two-factor enabled and have validated their
# token.
$result = true;
if ( $oathuser->getKey() !== null ) {
$result = $oathuser->getKey()->verifyToken( $token, $oathuser );
}
return $result;
}
/**
* Determine if two-factor authentication is enabled for $wgUser
*
* @param bool &$isEnabled Will be set to true if enabled, false otherwise
*
* @return bool False if enabled, true otherwise
*/
static function TwoFactorIsEnabled( &$isEnabled ) {
global $wgUser;
$user = self::getOATHUserRepository()->findByUser( $wgUser );
if ( $user && $user->getKey() !== null ) {
$isEnabled = true;
# This two-factor extension is enabled by the user,
# we don't need to check others.
if ( $oathuser->getKey() !== null && !$request->getCheck( 'token' ) ) {
$request->setSessionData( 'oath_login', $request->getValues() );
$output->redirect( SpecialPage::getTitleFor( 'OATH' )->getFullURL( '', false, PROTO_CURRENT ) );
return false;
} else {
$isEnabled = false;
# This two-factor extension isn't enabled by the user,
# but others may be.
return true;
}
}

View file

@ -16,6 +16,7 @@
"SpecialOATH": "special/SpecialOATH.php",
"SpecialOATHEnable": "special/SpecialOATHEnable.php",
"SpecialOATHDisable": "special/SpecialOATHDisable.php",
"SpecialOATHLogin": "special/SpecialOATHLogin.php",
"ProxySpecialPage": "special/ProxySpecialPage.php"
},
"ExtensionMessagesFiles": {
@ -28,15 +29,9 @@
"AbortLogin": [
"OATHAuthHooks::AbortLogin"
],
"UserLoginForm": [
"OATHAuthHooks::ModifyUITemplate"
],
"ChangePasswordForm": [
"OATHAuthHooks::ChangePasswordForm"
],
"TwoFactorIsEnabled": [
"OATHAuthHooks::TwoFactorIsEnabled"
],
"LoadExtensionSchemaUpdates": [
"OATHAuthHooks::OATHAuthSchemaUpdates"
],

View file

@ -17,11 +17,12 @@
"oathauth-verify": "Verify two-factor token",
"openstackmanager-scratchtokens": "The following list is a list of one-time use scratch tokens. These tokens can only be used once, and are for emergency use. Please write these down and keep them in a secure location. If you lose your phone, these tokens are the only way to rescue your account. These tokens will never be shown again.",
"oathauth-reset": "Reset two-factor credentials",
"oathauth-donotdeleteoldsecret": "Please do not delete your old credentials until you have validated your new credentials.",
"oathauth-donotdeleteoldsecret": "Please do not delete your old credentials until you have successfully validated your new credentials.",
"oathauth-token": "Token",
"oathauth-currenttoken": "Current token",
"oathauth-newtoken": "New token",
"oathauth-disable": "Disable two-factor authentication",
"oathauth-login": "Login with two-factor authentication",
"oathauth-displayoathinfo": "two-factor authentication options",
"oathauth-validatedoath": "Validated two-factor credentials. Two-factor authentication will now be enforced.",
"oathauth-backtopreferences": "Back to preferences.",
@ -36,6 +37,7 @@
"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",

View file

@ -26,6 +26,7 @@
"oathauth-currenttoken": "HTMLForm label, found on Special:OATH, when verifying OATH.",
"oathauth-newtoken": "HTMLForm label, found on Special:OATH, when verifying OATH.",
"oathauth-disable": "Page title on Special:OATH while disabling OATH.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
"oathauth-login": "Page title on Special:OATH while loggin in with OATH.",
"oathauth-displayoathinfo": "Page title on Special:OATH when no parameters are passed.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
"oathauth-validatedoath": "Plain text found on Special:OATH after a token has been validated.\n\nSee [https://en.wikipedia.org/wiki/Two_factor_authentication two factor authentication]",
"oathauth-backtopreferences": "Used as link text. Link found on Special:OATH after any action has completed.",

View file

@ -8,17 +8,37 @@ class SpecialOATH extends ProxySpecialPage {
* 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
*
* @return SpecialOATHDisable|SpecialOATHEnable|SpecialPage
* @return SpecialOATHDisable|SpecialOATHEnable|SpecialOATHLogin|SpecialPage
*/
protected function getTargetPage() {
$repo = OATHAuthHooks::getOATHUserRepository();
$user = $repo->findByUser( $this->getUser() );
if ( $user->getKey() === null ) {
return new SpecialOATHEnable( $repo, $user );
/** @var array $sessionUser */
$loginInfo = $this->getRequest()->getSessionData( 'oath_login' );
/** @var SpecialOATHDisable|SpecialOATHEnable|SpecialOATHLogin|SpecialPage $page */
$page = null;
if ( $this->getUser()->isAnon() && $loginInfo !== null ) {
// User is anonymous, so they are logging in
$page = new SpecialOATHLogin(
$repo->findByUser(User::newFromName( $loginInfo['wpName'] ) ),
new DerivativeRequest(
$this->getRequest(),
$loginInfo,
$this->getRequest()->wasPosted()
)
);
} else {
return new SpecialOATHDisable( $repo, $user );
$user = $repo->findByUser( $this->getUser() );
if ( $user->getKey() === null ) {
$page = new SpecialOATHEnable( $repo, $user );
} else {
$page = new SpecialOATHDisable( $repo, $user );
}
}
return $page;
}
protected function getGroupName() {

View file

@ -3,7 +3,6 @@
/**
* Special page to display key information to the user
*
* @file
* @ingroup Extensions
*/
class SpecialOATHDisable extends FormSpecialPage {

View file

@ -3,7 +3,6 @@
/**
* Special page to display key information to the user
*
* @file
* @ingroup Extensions
*/
class SpecialOATHEnable extends FormSpecialPage {

View file

@ -0,0 +1,124 @@
<?php
/**
* Special page to log users into two factor authentication
*/
class SpecialOATHLogin extends FormSpecialPage {
/** @var OATHUser|null */
private $OATHUser;
/** @var LoginForm */
private $loginForm;
/**
* @var string|null The token submitted by the user
*/
private $token = null;
/**
* Initialize the OATH user based on the current local User object in the context
*
* @param OATHUser $oathuser
* @param WebRequest $oldRequest
*/
public function __construct( OATHUser $oathuser, WebRequest $oldRequest ) {
Hooks::register( 'AbortLogin', $this );
parent::__construct( 'OATH', '', false );
$this->OATHUser = $oathuser;
$this->loginForm = new LoginForm( $oldRequest );
$this->loginForm->setContext( $this->getContext() );
}
/**
* 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-login' ) );
$form->getOutput()->addModules( 'ext.oathauth' );
}
/**
* @return string
*/
public function getDisplayFormat() {
return 'vform';
}
/**
* @return bool
*/
public function requiresUnblock() {
return false;
}
/**
* @return array[]
*/
protected function getFormFields() {
return array(
'token' => array(
'type' => 'text',
'default' => '',
'label-message' => 'oathauth-entertoken',
'name' => 'token',
'required' => true
),
'returnto' => array(
'type' => 'hidden',
'default' => $this->getRequest()->getVal( 'returnto' ),
'name' => 'returnto',
),
'returntoquery' => array(
'type' => 'hidden',
'default' => $this->getRequest()->getVal( 'returntoquery' ),
'name' => 'returntoquery',
)
);
}
/**
* Stub function: the only purpose of this form is to add more data into
* the login form
*
* @param array $formData
*
* @return true
*/
public function onSubmit( array $formData ) {
$this->getRequest()->setSessionData( 'oath_login', null );
$this->token = $formData['token'];
return true;
}
public function onSuccess() {
$this->loginForm->execute( $this->par );
}
/**
* @param User $user
* @param $password
* @param $abort
* @param $errorMsg
*
* @return bool
*/
public function onAbortLogin( User $user, $password, &$abort, &$errorMsg ) {
$result = $this->OATHUser
->getKey()
->verifyToken( $this->getRequest()->getVal( 'token' ), $this->OATHUser );
if ( $result ) {
return true;
} else {
$abort = LoginForm::WRONG_PASS;
return false;
}
}
}