*/
protected $par = null;
+ /**
+ * @var array|null POST data preserved across re-authentication
+ * @since 1.32
+ */
+ protected $reauthPostData = null;
+
/**
* Get an HTMLForm descriptor array
* @return array
* @return HTMLForm|null
*/
protected function getForm() {
+ $context = $this->getContext();
+ $onSubmit = [ $this, 'onSubmit' ];
+
+ if ( $this->reauthPostData ) {
+ // Restore POST data
+ $context = new DerivativeContext( $context );
+ $oldRequest = $this->getRequest();
+ $context->setRequest( new DerivativeRequest(
+ $oldRequest, $this->reauthPostData + $oldRequest->getQueryValues(), true
+ ) );
+
+ // But don't treat it as a "real" submission just in case of some
+ // crazy kind of CSRF.
+ $onSubmit = function () {
+ return false;
+ };
+ }
+
$form = HTMLForm::factory(
$this->getDisplayFormat(),
$this->getFormFields(),
- $this->getContext(),
+ $context,
$this->getMessagePrefix()
);
- $form->setSubmitCallback( [ $this, 'onSubmit' ] );
+ $form->setSubmitCallback( $onSubmit );
if ( $this->getDisplayFormat() !== 'ooui' ) {
// No legend and wrapper by default in OOUI forms, but can be set manually
// from alterForm()
// This will throw exceptions if there's a problem
$this->checkExecutePermissions( $this->getUser() );
+ $securityLevel = $this->getLoginSecurityLevel();
+ if ( $securityLevel !== false && !$this->checkLoginSecurityLevel( $securityLevel ) ) {
+ return;
+ }
+
$form = $this->getForm();
if ( $form->show() ) {
$this->onSuccess();
public function requiresUnblock() {
return true;
}
+
+ /**
+ * Preserve POST data across reauthentication
+ *
+ * @since 1.32
+ * @param array $data
+ */
+ protected function setReauthPostData( array $data ) {
+ $this->reauthPostData = $data;
+ }
}
return false;
}
+ /**
+ * Record preserved POST data after a reauthentication.
+ *
+ * This is called from checkLoginSecurityLevel() when returning from the
+ * redirect for reauthentication, if the redirect had been served in
+ * response to a POST request.
+ *
+ * The base SpecialPage implementation does nothing. If your subclass uses
+ * getLoginSecurityLevel() or checkLoginSecurityLevel(), it should probably
+ * implement this to do something with the data.
+ *
+ * @since 1.32
+ * @param array $data
+ */
+ protected function setReauthPostData( array $data ) {
+ }
+
/**
* Verifies that the user meets the security level, possibly reauthenticating them in the process.
*
*/
protected function checkLoginSecurityLevel( $level = null ) {
$level = $level ?: $this->getName();
+ $key = 'SpecialPage:reauth:' . $this->getName();
+ $request = $this->getRequest();
+
$securityStatus = AuthManager::singleton()->securitySensitiveOperationStatus( $level );
if ( $securityStatus === AuthManager::SEC_OK ) {
+ $uniqueId = $request->getVal( 'postUniqueId' );
+ if ( $uniqueId ) {
+ $key = $key . ':' . $uniqueId;
+ $session = $request->getSession();
+ $data = $session->getSecret( $key );
+ if ( $data ) {
+ $session->remove( $key );
+ $this->setReauthPostData( $data );
+ }
+ }
return true;
} elseif ( $securityStatus === AuthManager::SEC_REAUTH ) {
- $request = $this->getRequest();
$title = self::getTitleFor( 'Userlogin' );
+ $queryParams = $request->getQueryValues();
+
+ if ( $request->wasPosted() ) {
+ $data = array_diff_assoc( $request->getValues(), $request->getQueryValues() );
+ if ( $data ) {
+ // unique ID in case the same special page is open in multiple browser tabs
+ $uniqueId = MWCryptRand::generateHex( 6 );
+ $key = $key . ':' . $uniqueId;
+ $queryParams['postUniqueId'] = $uniqueId;
+ $session = $request->getSession();
+ $session->persist(); // Just in case
+ $session->setSecret( $key, $data );
+ }
+ }
+
$query = [
'returnto' => $this->getFullTitle()->getPrefixedDBkey(),
- 'returntoquery' => wfArrayToCgi( array_diff_key( $request->getQueryValues(),
- [ 'title' => true ] ) ),
+ 'returntoquery' => wfArrayToCgi( array_diff_key( $queryParams, [ 'title' => true ] ) ),
'force' => $level,
];
$url = $title->getFullURL( $query, false, PROTO_HTTPS );
public function execute( $subPage ) {
$this->setHeaders();
$this->checkPermissions();
- $this->checkLoginSecurityLevel( $this->getLoginSecurityLevel() );
+ $securityLevel = $this->getLoginSecurityLevel();
+ if ( $securityLevel !== false && !$this->checkLoginSecurityLevel( $securityLevel ) ) {
+ return;
+ }
$this->outputHeader();
}