wiki.techinc.nl/includes/auth/CheckBlocksSecondaryAuthenticationProvider.php
Gergő Tisza 04bdfa50f0
Fix block handling in CheckBlocksSecondaryAuthenticationProvider
The authentication provider's testUserForCreation() method is for
checking whether a given user name is available. The current
user being IP-blocked has nothing to do with that username's
availability so stop checking that. (AuthManager will check it
via AuthManager::authorizeCreateAccount() elsewhere. Although
that method doesn't seem to be doing anything useful and could
probably just be replaced with a direct call to
PermissionManager, but that's left for a separate, less risky
patch.)

Special-case autocreation though, which doesn't use
the more appropiate AuthManager::authorizeCreateAccount() for
performance reasons so it does need an IP block check.
(At least I think it is for performance reasons. Maybe it's
just an unintentional omission, and that should be used instead?)

While we are at it, also fix a TODO in AuthManager where partial
blocks were taken into account for $wgBlockDisablesLogin, and
clarify in the config schema that they aren't, improve some
comments to make it more obvious why some things are/aren't
done in CheckBlocksSecondaryAuthenticationProvider, and make
the logic more similar to the one in testUserForCreation().

Functional changes:
* Partial blocks are ignored for authentication, account
  creation and autocreation.
* On $wgBlockDisablesLogin wikis IP blocks won't prevent
  login anymore.
* On $wgBlockDisablesLogin wikis, blocks will now prevent
  account autocreation even if they are not configured to
  prevent account creation. The assumption is that on such
  wikis account creation is restricted via some means.
  This probably isn't necessary as blocks should also prevent
  the conditions needed for autocreation (e.g. log the user
  out centrally), but can serve as defense in depth.
  Along with the special-casing of autocreation, this means
  on such wikis any IP block will prevent autocreation, which
  is not great but seems not worth even more code complexity
  to avoid.
* The action=query&list=users&usprop=cancreate API won't take
  blocks into account anymore.

Bug: T306018
Bug: T208895
Change-Id: Ie94d61640301192b287275311f3452e606469d25
2022-08-30 19:06:16 -07:00

138 lines
5 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Auth
*/
namespace MediaWiki\Auth;
use DerivativeContext;
use MediaWiki\Block\AbstractBlock;
use MediaWiki\MainConfigNames;
use MediaWiki\MediaWikiServices;
use RequestContext;
use StatusValue;
/**
* Check if the user is blocked, and prevent authentication if so.
*
* Not all scenarios are covered by this class, AuthManager does some block checks itself
* via AuthManager::authorizeCreateAccount().
*
* @ingroup Auth
* @since 1.27
*/
class CheckBlocksSecondaryAuthenticationProvider extends AbstractSecondaryAuthenticationProvider {
/** @var bool */
protected $blockDisablesLogin = null;
/**
* @param array $params
* - blockDisablesLogin: (bool) Whether blocked accounts can log in,
* defaults to $wgBlockDisablesLogin
*/
public function __construct( $params = [] ) {
if ( isset( $params['blockDisablesLogin'] ) ) {
$this->blockDisablesLogin = (bool)$params['blockDisablesLogin'];
}
}
/** @inheritDoc */
protected function postInitSetup() {
if ( $this->blockDisablesLogin === null ) {
$this->blockDisablesLogin = $this->config->get( MainConfigNames::BlockDisablesLogin );
}
}
/** @inheritDoc */
public function getAuthenticationRequests( $action, array $options ) {
return [];
}
/** @inheritDoc */
public function beginSecondaryAuthentication( $user, array $reqs ) {
if ( !$this->blockDisablesLogin ) {
return AuthenticationResponse::newAbstain();
}
$block = $user->getBlock();
// Ignore IP blocks and partial blocks, $wgBlockDisablesLogin was meant for
// blocks banning specific users.
if ( $block && $block->isSitewide() && $block->isBlocking( $user ) ) {
return AuthenticationResponse::newFail(
new \Message( 'login-userblocked', [ $user->getName() ] )
);
} else {
return AuthenticationResponse::newPass();
}
}
/** @inheritDoc */
public function beginSecondaryAccountCreation( $user, $creator, array $reqs ) {
return AuthenticationResponse::newAbstain();
}
/** @inheritDoc */
public function testUserForCreation( $user, $autocreate, array $options = [] ) {
// isBlockedFromCreateAccount() does not return non-accountcreation blocks, but we need them
// in the $wgBlockDisablesLogin case; getBlock() is unreliable for IP blocks. So we need both.
$blocks = [
'local-createaccount' => $user->isBlockedFromCreateAccount(),
'local' => $user->getBlock(),
];
foreach ( $blocks as $type => $block ) {
/** @var AbstractBlock $block */
if ( $block && $block->isSitewide()
// This method is for checking a given account/username, not the current user, so
// ignore IP blocks; they will be checked elsewhere via authorizeCreateAccount().
// FIXME: special-case autocreation which doesn't do that check. Should it?
&& ( $block->isBlocking( $user ) || $autocreate )
&& (
// Should blocks that prevent account creation also prevent autocreation?
// We'll go with yes here.
$block->isCreateAccountBlocked()
// A successful autocreation means the user is logged in, so we must make sure to
// honor $wgBlockDisablesLogin. If it's enabled, sitewide blocks are expected to
// prevent login regardless of their flags.
|| ( $autocreate && $this->blockDisablesLogin )
)
// FIXME: ideally on autocreation we'd figure out if the user has the ipblock-exempt
// or globalblock-exempt right via some central authorization system like
// CentralAuth global groups. But at this point the local account doesn't exist
// yet so there is no way to do that. There should probably be some separate hook
// to fetch user rights for a central user.
// FIXME: T249444: there should probably be a way to force autocreation through blocks
) {
$language = \RequestContext::getMain()->getUser()->isSafeToLoad() ?
\RequestContext::getMain()->getLanguage() :
MediaWikiServices::getInstance()->getContentLanguage();
// Block::getPermissionsError() is deprecated but actually works for global blocks,
// unlike its replacement.
$context = new DerivativeContext( RequestContext::getMain() );
$context->setLanguage( $language );
$context->setUser( $user );
return StatusValue::newFatal(
...$block->getPermissionsError( $context )
);
}
}
return StatusValue::newGood();
}
}