* @ingroup SpecialPage
*/
+use MediaWiki\Auth\AuthManager;
+
/**
* Parent class for all special pages.
*
}
}
+ /**
+ * Tells if the special page does something security-sensitive and needs extra defense against
+ * a stolen account (e.g. a reauthentication). What exactly that will mean is decided by the
+ * authentication framework.
+ * @return bool|string False or the argument for AuthManager::securitySensitiveOperationStatus().
+ * Typically a special page needing elevated security would return its name here.
+ */
+ protected function getLoginSecurityLevel() {
+ return false;
+ }
+
+ /**
+ * Verifies that the user meets the security level, possibly reauthenticating them in the process.
+ *
+ * This should be used when the page does something security-sensitive and needs extra defense
+ * against a stolen account (e.g. a reauthentication). The authentication framework will make
+ * an extra effort to make sure the user account is not compromised. What that exactly means
+ * will depend on the system and user settings; e.g. the user might be required to log in again
+ * unless their last login happened recently, or they might be given a second-factor challenge.
+ *
+ * Calling this method will result in one if these actions:
+ * - return true: all good.
+ * - return false and set a redirect: caller should abort; the redirect will take the user
+ * to the login page for reauthentication, and back.
+ * - throw an exception if there is no way for the user to meet the requirements without using
+ * a different access method (e.g. this functionality is only available from a specific IP).
+ *
+ * Note that this does not in any way check that the user is authorized to use this special page
+ * (use checkPermissions() for that).
+ *
+ * @param string $level A security level. Can be an arbitrary string, defaults to the page name.
+ * @return bool False means a redirect to the reauthentication page has been set and processing
+ * of the special page should be aborted.
+ * @throws ErrorPageError If the security level cannot be met, even with reauthentication.
+ */
+ protected function checkLoginSecurityLevel( $level = null ) {
+ $level = $level ?: $this->getName();
+ $securityStatus = AuthManager::singleton()->securitySensitiveOperationStatus( $level );
+ if ( $securityStatus === AuthManager::SEC_OK ) {
+ return true;
+ } elseif ( $securityStatus === AuthManager::SEC_REAUTH ) {
+ $request = $this->getRequest();
+ $title = SpecialPage::getTitleFor( 'Userlogin' );
+ $query = [
+ 'returnto' => $this->getFullTitle()->getPrefixedDBkey(),
+ 'returntoquery' => wfArrayToCgi( array_diff_key( $request->getQueryValues(),
+ [ 'title' => true ] ) ),
+ 'force' => $level,
+ ];
+ $url = $title->getFullURL( $query, false, PROTO_HTTPS );
+
+ $this->getOutput()->redirect( $url );
+ return false;
+ }
+
+ $titleMessage = wfMessage( 'specialpage-securitylevel-not-allowed-title' );
+ $errorMessage = wfMessage( 'specialpage-securitylevel-not-allowed' );
+ throw new ErrorPageError( $titleMessage, $errorMessage );
+ }
+
/**
* Return an array of subpages beginning with $search that this special page will accept.
*
public function execute( $subPage ) {
$this->setHeaders();
$this->checkPermissions();
+ $this->checkLoginSecurityLevel( $this->getLoginSecurityLevel() );
$this->outputHeader();
}