2007-12-08 21:06:21 +00:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Hooks for Title Blacklist
|
2010-11-08 21:55:05 +00:00
|
|
|
* @author Victor Vasiliev
|
|
|
|
* @copyright © 2007-2010 Victor Vasiliev et al
|
2018-04-07 01:23:42 +00:00
|
|
|
* @license GPL-2.0-or-later
|
2007-12-08 21:06:21 +00:00
|
|
|
*/
|
2008-08-09 01:54:39 +00:00
|
|
|
|
2022-04-08 13:20:08 +00:00
|
|
|
namespace MediaWiki\Extension\TitleBlacklist;
|
|
|
|
|
|
|
|
use ApiMessage;
|
|
|
|
use ApiResult;
|
|
|
|
use ManualLogEntry;
|
2024-06-10 20:05:31 +00:00
|
|
|
use MediaWiki\Context\RequestContext;
|
2023-05-06 21:20:41 +00:00
|
|
|
use MediaWiki\EditPage\EditPage;
|
2022-04-08 13:20:08 +00:00
|
|
|
use MediaWiki\Hook\EditFilterHook;
|
|
|
|
use MediaWiki\Hook\MovePageCheckPermissionsHook;
|
|
|
|
use MediaWiki\Hook\TitleGetEditNoticesHook;
|
2024-01-04 21:31:38 +00:00
|
|
|
use MediaWiki\Html\Html;
|
2023-11-03 06:37:19 +00:00
|
|
|
use MediaWiki\Permissions\GrantsInfo;
|
2022-04-08 13:20:08 +00:00
|
|
|
use MediaWiki\Permissions\Hook\GetUserPermissionsErrorsExpensiveHook;
|
2020-06-16 04:30:26 +00:00
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
2024-01-04 21:31:38 +00:00
|
|
|
use MediaWiki\Status\Status;
|
2020-06-16 04:30:26 +00:00
|
|
|
use MediaWiki\Storage\EditResult;
|
2022-04-08 13:20:08 +00:00
|
|
|
use MediaWiki\Storage\Hook\PageSaveCompleteHook;
|
2023-08-19 04:20:04 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2024-01-04 21:31:38 +00:00
|
|
|
use MediaWiki\User\User;
|
2020-06-16 04:30:26 +00:00
|
|
|
use MediaWiki\User\UserIdentity;
|
2022-04-08 13:20:08 +00:00
|
|
|
use MessageSpecifier;
|
|
|
|
use StatusValue;
|
|
|
|
use WikiPage;
|
2020-06-16 04:30:26 +00:00
|
|
|
|
2008-08-09 01:54:39 +00:00
|
|
|
/**
|
|
|
|
* Hooks for the TitleBlacklist class
|
2010-06-06 15:12:22 +00:00
|
|
|
*
|
|
|
|
* @ingroup Extensions
|
2008-08-09 01:54:39 +00:00
|
|
|
*/
|
2022-04-08 13:20:08 +00:00
|
|
|
class Hooks implements
|
|
|
|
EditFilterHook,
|
|
|
|
TitleGetEditNoticesHook,
|
|
|
|
MovePageCheckPermissionsHook,
|
|
|
|
GetUserPermissionsErrorsExpensiveHook,
|
|
|
|
PageSaveCompleteHook
|
2021-03-07 11:31:24 +00:00
|
|
|
{
|
2011-03-14 20:51:13 +00:00
|
|
|
|
2023-11-03 06:37:19 +00:00
|
|
|
public static function onRegistration() {
|
|
|
|
global $wgGrantRiskGroups;
|
|
|
|
// Make sure the risk rating is at least 'security'. TitleBlacklist adds the
|
|
|
|
// tboverride-account right to the createaccount grant, which makes it possible
|
|
|
|
// to use it for social engineering attacks with restricted usernames.
|
|
|
|
if ( $wgGrantRiskGroups['createaccount'] !== GrantsInfo::RISK_INTERNAL ) {
|
|
|
|
$wgGrantRiskGroups['createaccount'] = GrantsInfo::RISK_SECURITY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-14 20:51:13 +00:00
|
|
|
/**
|
|
|
|
* getUserPermissionsErrorsExpensive hook
|
|
|
|
*
|
2017-08-10 11:26:29 +00:00
|
|
|
* @param Title $title
|
|
|
|
* @param User $user
|
|
|
|
* @param string $action
|
2021-03-07 11:31:24 +00:00
|
|
|
* @param array|string|MessageSpecifier &$result
|
2019-05-28 21:37:57 +00:00
|
|
|
*
|
2011-03-14 20:51:13 +00:00
|
|
|
* @return bool
|
|
|
|
*/
|
2021-03-07 11:31:24 +00:00
|
|
|
public function onGetUserPermissionsErrorsExpensive( $title, $user, $action, &$result ) {
|
2011-06-24 21:25:50 +00:00
|
|
|
# Some places check createpage, while others check create.
|
|
|
|
# As it stands, upload does createpage, but normalize both
|
|
|
|
# to the same action, to stop future similar bugs.
|
2012-08-29 13:53:38 +00:00
|
|
|
if ( $action === 'createpage' || $action === 'createtalk' ) {
|
2011-06-24 21:25:50 +00:00
|
|
|
$action = 'create';
|
|
|
|
}
|
2022-09-29 14:12:21 +00:00
|
|
|
if ( $action !== 'create' && $action !== 'edit' && $action !== 'upload' ) {
|
|
|
|
return true;
|
2007-12-08 21:06:21 +00:00
|
|
|
}
|
2022-09-29 14:12:21 +00:00
|
|
|
$blacklisted = TitleBlacklist::singleton()->userCannot( $title, $user, $action );
|
|
|
|
if ( !$blacklisted ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$errmsg = $blacklisted->getErrorMessage( 'edit' );
|
|
|
|
$params = [
|
|
|
|
$blacklisted->getRaw(),
|
|
|
|
$title->getFullText()
|
|
|
|
];
|
|
|
|
ApiResult::setIndexedTagName( $params, 'param' );
|
|
|
|
$result = ApiMessage::create(
|
|
|
|
wfMessage(
|
|
|
|
$errmsg,
|
2023-11-02 20:09:37 +00:00
|
|
|
wfEscapeWikiText( $blacklisted->getRaw() ),
|
2022-09-29 14:12:21 +00:00
|
|
|
$title->getFullText()
|
|
|
|
),
|
|
|
|
'titleblacklist-forbidden',
|
|
|
|
[
|
|
|
|
'message' => [
|
|
|
|
'key' => $errmsg,
|
|
|
|
'params' => $params,
|
|
|
|
],
|
|
|
|
'line' => $blacklisted->getRaw(),
|
|
|
|
// As $errmsg usually represents a non-default message here, and ApiBase
|
|
|
|
// uses ->inLanguage( 'en' )->useDatabase( false ) for all messages, it will
|
|
|
|
// never result in useful 'info' text in the API. Try this, extra data seems
|
|
|
|
// to override the default.
|
|
|
|
'info' => 'TitleBlacklist prevents this title from being created',
|
|
|
|
]
|
|
|
|
);
|
|
|
|
return false;
|
2007-12-08 21:06:21 +00:00
|
|
|
}
|
|
|
|
|
2014-06-19 17:34:57 +00:00
|
|
|
/**
|
|
|
|
* Display a notice if a user is only able to create or edit a page
|
2015-09-11 00:09:14 +00:00
|
|
|
* because they have tboverride.
|
2014-06-19 17:34:57 +00:00
|
|
|
*
|
|
|
|
* @param Title $title
|
2017-08-10 11:26:29 +00:00
|
|
|
* @param int $oldid
|
2014-06-19 17:34:57 +00:00
|
|
|
* @param array &$notices
|
|
|
|
*/
|
2021-03-07 11:31:24 +00:00
|
|
|
public function onTitleGetEditNotices( $title, $oldid, &$notices ) {
|
2015-09-11 00:09:14 +00:00
|
|
|
if ( !RequestContext::getMain()->getUser()->isAllowed( 'tboverride' ) ) {
|
2019-05-28 21:37:57 +00:00
|
|
|
return;
|
2015-09-11 00:09:14 +00:00
|
|
|
}
|
|
|
|
|
2014-06-19 17:34:57 +00:00
|
|
|
$blacklisted = TitleBlacklist::singleton()->isBlacklisted(
|
|
|
|
$title,
|
|
|
|
$title->exists() ? 'edit' : 'create'
|
|
|
|
);
|
2015-09-11 00:09:14 +00:00
|
|
|
if ( !$blacklisted ) {
|
2019-05-28 21:37:57 +00:00
|
|
|
return;
|
2014-06-19 17:34:57 +00:00
|
|
|
}
|
2015-09-11 00:09:14 +00:00
|
|
|
|
|
|
|
$params = $blacklisted->getParams();
|
2016-06-15 02:42:22 +00:00
|
|
|
if ( isset( $params['autoconfirmed'] ) ) {
|
2019-05-28 21:37:57 +00:00
|
|
|
return;
|
2016-06-15 02:42:22 +00:00
|
|
|
}
|
|
|
|
|
2015-09-11 00:09:14 +00:00
|
|
|
$msg = wfMessage( 'titleblacklist-warning' );
|
2019-05-28 21:37:57 +00:00
|
|
|
$notices['titleblacklist'] = $msg->plaintextParams( $blacklisted->getRaw() )
|
|
|
|
->parseAsBlock();
|
2014-06-19 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2011-03-14 20:51:13 +00:00
|
|
|
/**
|
2014-10-27 18:06:03 +00:00
|
|
|
* MovePageCheckPermissions hook (1.25+)
|
2011-03-14 20:51:13 +00:00
|
|
|
*
|
2014-10-27 18:06:03 +00:00
|
|
|
* @param Title $oldTitle
|
|
|
|
* @param Title $newTitle
|
|
|
|
* @param User $user
|
2017-08-10 11:26:29 +00:00
|
|
|
* @param string $reason
|
2014-10-27 18:06:03 +00:00
|
|
|
* @param Status $status
|
2019-05-28 21:37:57 +00:00
|
|
|
*
|
2011-03-14 20:51:13 +00:00
|
|
|
* @return bool
|
|
|
|
*/
|
2021-03-07 11:31:24 +00:00
|
|
|
public function onMovePageCheckPermissions(
|
|
|
|
$oldTitle, $newTitle, $user, $reason, $status
|
2017-06-02 15:43:58 +00:00
|
|
|
) {
|
2011-07-30 15:13:28 +00:00
|
|
|
$titleBlacklist = TitleBlacklist::singleton();
|
2014-10-27 18:06:03 +00:00
|
|
|
$blacklisted = $titleBlacklist->userCannot( $newTitle, $user, 'move' );
|
2012-08-29 13:53:38 +00:00
|
|
|
if ( !$blacklisted ) {
|
2014-10-27 18:06:03 +00:00
|
|
|
$blacklisted = $titleBlacklist->userCannot( $oldTitle, $user, 'edit' );
|
2011-03-14 20:51:13 +00:00
|
|
|
}
|
2012-08-29 13:53:38 +00:00
|
|
|
if ( $blacklisted instanceof TitleBlacklistEntry ) {
|
2016-11-03 19:16:57 +00:00
|
|
|
$status->fatal( ApiMessage::create( [
|
|
|
|
$blacklisted->getErrorMessage( 'move' ),
|
2023-11-02 20:09:37 +00:00
|
|
|
wfEscapeWikiText( $blacklisted->getRaw() ),
|
2014-10-27 18:06:03 +00:00
|
|
|
$oldTitle->getFullText(),
|
2016-11-03 19:16:57 +00:00
|
|
|
$newTitle->getFullText()
|
|
|
|
] ) );
|
2007-12-08 21:06:21 +00:00
|
|
|
return false;
|
|
|
|
}
|
2014-10-27 18:06:03 +00:00
|
|
|
|
2007-12-08 21:06:21 +00:00
|
|
|
return true;
|
|
|
|
}
|
2008-08-09 01:54:39 +00:00
|
|
|
|
2016-05-07 11:21:02 +00:00
|
|
|
/**
|
|
|
|
* Check whether a user name is acceptable for account creation or autocreation, and explain
|
|
|
|
* why not if that's the case.
|
|
|
|
*
|
|
|
|
* @param string $userName
|
|
|
|
* @param User $creatingUser
|
|
|
|
* @param bool $override Should the test be skipped, if the user has sufficient privileges?
|
|
|
|
* @param bool $log Log blacklist hits to Special:Log
|
2019-05-28 21:37:57 +00:00
|
|
|
*
|
2016-05-07 11:21:02 +00:00
|
|
|
* @return StatusValue
|
|
|
|
*/
|
2017-06-02 15:43:58 +00:00
|
|
|
public static function testUserName(
|
|
|
|
$userName, User $creatingUser, $override = true, $log = false
|
|
|
|
) {
|
2010-08-12 09:28:30 +00:00
|
|
|
$title = Title::makeTitleSafe( NS_USER, $userName );
|
2016-05-07 11:21:02 +00:00
|
|
|
$blacklisted = TitleBlacklist::singleton()->userCannot( $title, $creatingUser,
|
2011-09-22 06:10:45 +00:00
|
|
|
'new-account', $override );
|
2022-09-29 14:12:21 +00:00
|
|
|
if ( !$blacklisted ) {
|
|
|
|
return StatusValue::newGood();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $log ) {
|
|
|
|
self::logFilterHitUsername( $creatingUser, $title, $blacklisted->getRaw() );
|
2008-08-09 01:54:39 +00:00
|
|
|
}
|
2022-09-29 14:12:21 +00:00
|
|
|
$message = $blacklisted->getErrorMessage( 'new-account' );
|
|
|
|
$params = [
|
|
|
|
$blacklisted->getRaw(),
|
|
|
|
$userName,
|
|
|
|
];
|
|
|
|
ApiResult::setIndexedTagName( $params, 'param' );
|
|
|
|
return StatusValue::newFatal( ApiMessage::create(
|
2023-11-02 20:09:37 +00:00
|
|
|
[ $message, wfEscapeWikiText( $blacklisted->getRaw() ), $userName ],
|
2022-09-29 14:12:21 +00:00
|
|
|
'titleblacklist-forbidden',
|
|
|
|
[
|
|
|
|
'message' => [
|
|
|
|
'key' => $message,
|
|
|
|
'params' => $params,
|
|
|
|
],
|
|
|
|
'line' => $blacklisted->getRaw(),
|
|
|
|
// The text of the message probably isn't useful API info, so do this instead
|
|
|
|
'info' => 'TitleBlacklist prevents this username from being created',
|
|
|
|
]
|
|
|
|
) );
|
2008-08-09 01:54:39 +00:00
|
|
|
}
|
2010-06-04 13:36:29 +00:00
|
|
|
|
2011-08-22 20:37:26 +00:00
|
|
|
/**
|
|
|
|
* EditFilter hook
|
2011-03-17 22:20:01 +00:00
|
|
|
*
|
2017-08-10 11:26:29 +00:00
|
|
|
* @param EditPage $editor
|
|
|
|
* @param string $text
|
|
|
|
* @param string $section
|
|
|
|
* @param string &$error
|
2021-03-07 11:31:24 +00:00
|
|
|
* @param string $summary
|
2011-03-17 22:20:01 +00:00
|
|
|
*/
|
2021-03-07 11:31:24 +00:00
|
|
|
public function onEditFilter( $editor, $text, $section, &$error, $summary ) {
|
2017-09-03 07:33:59 +00:00
|
|
|
$title = $editor->getTitle();
|
2011-03-17 22:20:01 +00:00
|
|
|
|
2012-08-29 13:53:38 +00:00
|
|
|
if ( $title->getNamespace() == NS_MEDIAWIKI && $title->getDBkey() == 'Titleblacklist' ) {
|
2011-07-30 15:13:28 +00:00
|
|
|
$blackList = TitleBlacklist::singleton();
|
2019-04-27 04:30:31 +00:00
|
|
|
$bl = TitleBlacklist::parseBlacklist( $text, 'page' );
|
2011-07-30 15:13:28 +00:00
|
|
|
$ok = $blackList->validate( $bl );
|
2019-05-28 21:37:57 +00:00
|
|
|
if ( $ok === [] ) {
|
|
|
|
return;
|
2008-09-14 01:44:04 +00:00
|
|
|
}
|
|
|
|
|
2012-10-20 14:46:01 +00:00
|
|
|
$errmsg = wfMessage( 'titleblacklist-invalid' )->numParams( count( $ok ) )->text();
|
2017-06-02 15:43:58 +00:00
|
|
|
$errlines = '* <code>' .
|
|
|
|
implode( "</code>\n* <code>", array_map( 'wfEscapeWikiText', $ok ) ) .
|
|
|
|
'</code>';
|
2022-03-25 14:05:59 +00:00
|
|
|
$error = Html::errorBox(
|
|
|
|
$errmsg . "\n" . $errlines
|
|
|
|
) . "\n" .
|
2017-06-06 16:18:36 +00:00
|
|
|
Html::element( 'br', [ 'clear' => 'all' ] ) . "\n";
|
2010-08-12 08:38:05 +00:00
|
|
|
|
2008-09-14 01:44:04 +00:00
|
|
|
// $error will be displayed by the edit class
|
2007-12-23 09:47:36 +00:00
|
|
|
}
|
2007-12-29 04:23:57 +00:00
|
|
|
}
|
2010-08-12 08:38:05 +00:00
|
|
|
|
2011-08-22 20:37:26 +00:00
|
|
|
/**
|
2020-06-16 04:30:26 +00:00
|
|
|
* PageSaveComplete hook
|
2011-03-17 22:20:01 +00:00
|
|
|
*
|
2019-05-28 21:37:57 +00:00
|
|
|
* @param WikiPage $wikiPage
|
2020-06-16 04:30:26 +00:00
|
|
|
* @param UserIdentity $userIdentity
|
2017-08-10 11:26:29 +00:00
|
|
|
* @param string $summary
|
2020-06-16 04:30:26 +00:00
|
|
|
* @param int $flags
|
|
|
|
* @param RevisionRecord $revisionRecord
|
|
|
|
* @param EditResult $editResult
|
2011-03-17 22:20:01 +00:00
|
|
|
*/
|
2021-03-07 11:31:24 +00:00
|
|
|
public function onPageSaveComplete(
|
|
|
|
$wikiPage,
|
|
|
|
$userIdentity,
|
|
|
|
$summary,
|
|
|
|
$flags,
|
|
|
|
$revisionRecord,
|
|
|
|
$editResult
|
2017-06-06 16:18:36 +00:00
|
|
|
) {
|
2017-12-08 14:15:33 +00:00
|
|
|
$title = $wikiPage->getTitle();
|
2019-05-28 21:37:57 +00:00
|
|
|
if ( $title->getNamespace() === NS_MEDIAWIKI && $title->getDBkey() == 'Titleblacklist' ) {
|
2011-07-30 15:13:28 +00:00
|
|
|
TitleBlacklist::singleton()->invalidate();
|
2007-12-29 04:23:57 +00:00
|
|
|
}
|
2007-12-23 09:47:36 +00:00
|
|
|
}
|
2010-11-08 21:55:05 +00:00
|
|
|
|
2014-05-23 19:02:49 +00:00
|
|
|
/**
|
|
|
|
* Logs the filter username hit to Special:Log if
|
|
|
|
* $wgTitleBlacklistLogHits is enabled.
|
|
|
|
*
|
|
|
|
* @param User $user
|
|
|
|
* @param Title $title
|
|
|
|
* @param string $entry
|
|
|
|
*/
|
|
|
|
public static function logFilterHitUsername( $user, $title, $entry ) {
|
|
|
|
global $wgTitleBlacklistLogHits;
|
|
|
|
if ( $wgTitleBlacklistLogHits ) {
|
|
|
|
$logEntry = new ManualLogEntry( 'titleblacklist', 'hit-username' );
|
|
|
|
$logEntry->setPerformer( $user );
|
|
|
|
$logEntry->setTarget( $title );
|
2017-06-06 16:18:36 +00:00
|
|
|
$logEntry->setParameters( [
|
2014-05-23 19:02:49 +00:00
|
|
|
'4::entry' => $entry,
|
2017-06-06 16:18:36 +00:00
|
|
|
] );
|
2014-05-23 19:02:49 +00:00
|
|
|
$logid = $logEntry->insert();
|
|
|
|
$logEntry->publish( $logid );
|
|
|
|
}
|
|
|
|
}
|
2008-09-15 16:33:40 +00:00
|
|
|
}
|