Viewing File: /home/omtekel/www/wp-content/upgrade/backup/Handlers.tar
FilteredActionsHandler.php 0000666 00000025622 15133476563 0011662 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use ApiMessage;
use Content;
use DeferredUpdates;
use IBufferingStatsdDataFactory;
use IContextSource;
use MediaWiki\Extension\AbuseFilter\BlockedDomainFilter;
use MediaWiki\Extension\AbuseFilter\EditRevUpdater;
use MediaWiki\Extension\AbuseFilter\FilterRunnerFactory;
use MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGeneratorFactory;
use MediaWiki\Hook\EditFilterMergedContentHook;
use MediaWiki\Hook\TitleMoveHook;
use MediaWiki\Hook\UploadStashFileHook;
use MediaWiki\Hook\UploadVerifyUploadHook;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Page\Hook\ArticleDeleteHook;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Revision\SlotRecord;
use MediaWiki\Storage\Hook\ParserOutputStashForEditHook;
use MediaWiki\Title\Title;
use Status;
use UploadBase;
use User;
use WikiPage;
/**
* Handler for actions that can be filtered
*/
class FilteredActionsHandler implements
EditFilterMergedContentHook,
TitleMoveHook,
ArticleDeleteHook,
UploadVerifyUploadHook,
UploadStashFileHook,
ParserOutputStashForEditHook
{
/** @var IBufferingStatsdDataFactory */
private $statsDataFactory;
/** @var FilterRunnerFactory */
private $filterRunnerFactory;
/** @var VariableGeneratorFactory */
private $variableGeneratorFactory;
/** @var EditRevUpdater */
private $editRevUpdater;
private PermissionManager $permissionManager;
private BlockedDomainFilter $blockedDomainFilter;
/**
* @param IBufferingStatsdDataFactory $statsDataFactory
* @param FilterRunnerFactory $filterRunnerFactory
* @param VariableGeneratorFactory $variableGeneratorFactory
* @param EditRevUpdater $editRevUpdater
* @param BlockedDomainFilter $blockedDomainFilter
* @param PermissionManager $permissionManager
*/
public function __construct(
IBufferingStatsdDataFactory $statsDataFactory,
FilterRunnerFactory $filterRunnerFactory,
VariableGeneratorFactory $variableGeneratorFactory,
EditRevUpdater $editRevUpdater,
BlockedDomainFilter $blockedDomainFilter,
PermissionManager $permissionManager
) {
$this->statsDataFactory = $statsDataFactory;
$this->filterRunnerFactory = $filterRunnerFactory;
$this->variableGeneratorFactory = $variableGeneratorFactory;
$this->editRevUpdater = $editRevUpdater;
$this->blockedDomainFilter = $blockedDomainFilter;
$this->permissionManager = $permissionManager;
}
/**
* @inheritDoc
* @param string $slot Slot role for the content, added by Wikibase (T288885)
*/
public function onEditFilterMergedContent(
IContextSource $context,
Content $content,
Status $status,
$summary,
User $user,
$minoredit,
string $slot = SlotRecord::MAIN
) {
$startTime = microtime( true );
if ( !$status->isOK() ) {
// Investigate what happens if we skip filtering here (T211680)
LoggerFactory::getInstance( 'AbuseFilter' )->info(
'Status is already not OK',
[ 'status' => (string)$status ]
);
}
$filterResult = $this->filterEdit( $context, $user, $content, $summary, $slot );
if ( !$filterResult->isOK() ) {
// Produce a useful error message for API edits
$filterResultApi = self::getApiStatus( $filterResult );
$status->merge( $filterResultApi );
}
$this->statsDataFactory->timing( 'timing.editAbuseFilter', microtime( true ) - $startTime );
return $status->isOK();
}
/**
* Implementation for EditFilterMergedContent hook.
*
* @param IContextSource $context the context of the edit
* @param User $user
* @param Content $content the new Content generated by the edit
* @param string $summary Edit summary for page
* @param string $slot slot role for the content
* @return Status
*/
private function filterEdit(
IContextSource $context,
User $user,
Content $content,
string $summary,
string $slot = SlotRecord::MAIN
): Status {
$this->editRevUpdater->clearLastEditPage();
$title = $context->getTitle();
$logger = LoggerFactory::getInstance( 'AbuseFilter' );
if ( $title === null ) {
// T144265: This *should* never happen.
$logger->warning( __METHOD__ . ' received a null title.' );
return Status::newGood();
}
if ( !$title->canExist() ) {
// This also should be handled in EditPage or whoever is calling the hook.
$logger->warning( __METHOD__ . ' received a Title that cannot exist.' );
// Note that if the title cannot exist, there's no much point in filtering the edit anyway
return Status::newGood();
}
$page = $context->getWikiPage();
$builder = $this->variableGeneratorFactory->newRunGenerator( $user, $title );
$vars = $builder->getEditVars( $content, $summary, $slot, $page );
if ( $vars === null ) {
// We don't have to filter the edit
return Status::newGood();
}
$runner = $this->filterRunnerFactory->newRunner( $user, $title, $vars, 'default' );
$filterResult = $runner->run();
if ( !$filterResult->isOK() ) {
return $filterResult;
}
$this->editRevUpdater->setLastEditPage( $page );
if ( $this->permissionManager->userHasRight( $user, 'abusefilter-bypass-blocked-external-domains' ) ) {
return Status::newGood();
}
$blockedDomainFilterResult = $this->blockedDomainFilter->filter( $vars, $user, $title );
if ( $blockedDomainFilterResult instanceof Status ) {
return $blockedDomainFilterResult;
}
return Status::newGood();
}
/**
* @param Status $status Error message details
* @return Status Status containing the same error messages with extra data for the API
*/
private static function getApiStatus( Status $status ): Status {
$allActionsTaken = $status->getValue();
$statusForApi = Status::newGood();
foreach ( $status->getErrors() as $error ) {
[ $filterDescription, $filter ] = $error['params'];
$actionsTaken = $allActionsTaken[ $filter ];
$code = ( $actionsTaken === [ 'warn' ] ) ? 'abusefilter-warning' : 'abusefilter-disallowed';
$data = [
'abusefilter' => [
'id' => $filter,
'description' => $filterDescription,
'actions' => $actionsTaken,
],
];
$message = ApiMessage::create( $error, $code, $data );
$statusForApi->fatal( $message );
}
return $statusForApi;
}
/**
* @inheritDoc
*/
public function onTitleMove( Title $old, Title $nt, User $user, $reason, Status &$status ) {
$builder = $this->variableGeneratorFactory->newRunGenerator( $user, $old );
$vars = $builder->getMoveVars( $nt, $reason );
$runner = $this->filterRunnerFactory->newRunner( $user, $old, $vars, 'default' );
$result = $runner->run();
$status->merge( $result );
}
/**
* @inheritDoc
*/
public function onArticleDelete( WikiPage $wikiPage, User $user, &$reason, &$error, Status &$status, $suppress ) {
if ( $suppress ) {
// Don't filter suppressions, T71617
return true;
}
$builder = $this->variableGeneratorFactory->newRunGenerator( $user, $wikiPage->getTitle() );
$vars = $builder->getDeleteVars( $reason );
$runner = $this->filterRunnerFactory->newRunner( $user, $wikiPage->getTitle(), $vars, 'default' );
$filterResult = $runner->run();
$status->merge( $filterResult );
$error = $filterResult->isOK() ? '' : $filterResult->getHTML();
return $filterResult->isOK();
}
/**
* @inheritDoc
*/
public function onUploadVerifyUpload(
UploadBase $upload,
User $user,
?array $props,
$comment,
$pageText,
&$error
) {
return $this->filterUpload( 'upload', $upload, $user, $props, $comment, $pageText, $error );
}
/**
* Filter an upload to stash. If a filter doesn't need to check the page contents or
* upload comment, it can use `action='stashupload'` to provide better experience to e.g.
* UploadWizard (rejecting files immediately, rather than after the user adds the details).
*
* @inheritDoc
*/
public function onUploadStashFile( UploadBase $upload, User $user, ?array $props, &$error ) {
return $this->filterUpload( 'stashupload', $upload, $user, $props, null, null, $error );
}
/**
* Implementation for UploadStashFile and UploadVerifyUpload hooks.
*
* @param string $action 'upload' or 'stashupload'
* @param UploadBase $upload
* @param User $user User performing the action
* @param array|null $props File properties, as returned by MWFileProps::getPropsFromPath().
* @param string|null $summary Upload log comment (also used as edit summary)
* @param string|null $text File description page text (only used for new uploads)
* @param array|ApiMessage &$error
* @return bool
*/
private function filterUpload(
string $action,
UploadBase $upload,
User $user,
?array $props,
?string $summary,
?string $text,
&$error
): bool {
$title = $upload->getTitle();
if ( $title === null ) {
// T144265: This could happen for 'stashupload' if the specified title is invalid.
// Let UploadBase warn the user about that, and we'll filter later.
$logger = LoggerFactory::getInstance( 'AbuseFilter' );
$logger->warning( __METHOD__ . " received a null title. Action: $action." );
return true;
}
$builder = $this->variableGeneratorFactory->newRunGenerator( $user, $title );
$vars = $builder->getUploadVars( $action, $upload, $summary, $text, $props );
if ( $vars === null ) {
return true;
}
$runner = $this->filterRunnerFactory->newRunner( $user, $title, $vars, 'default' );
$filterResult = $runner->run();
if ( !$filterResult->isOK() ) {
// Produce a useful error message for API edits
$filterResultApi = self::getApiStatus( $filterResult );
// @todo Return all errors instead of only the first one
$error = $filterResultApi->getErrors()[0]['message'];
} else {
if ( $this->permissionManager->userHasRight( $user, 'abusefilter-bypass-blocked-external-domains' ) ) {
return true;
}
$blockedDomainFilterResult = $this->blockedDomainFilter->filter( $vars, $user, $title );
if ( $blockedDomainFilterResult instanceof Status ) {
$error = $blockedDomainFilterResult->getErrors()[0]['message'];
return $blockedDomainFilterResult->isOK();
}
}
return $filterResult->isOK();
}
/**
* @inheritDoc
*/
public function onParserOutputStashForEdit( $page, $content, $output, $summary, $user ) {
// XXX: This makes the assumption that this method is only ever called for the main slot.
// Which right now holds true, but any more fancy MCR stuff will likely break here...
$slot = SlotRecord::MAIN;
// Cache any resulting filter matches.
// Do this outside the synchronous stash lock to avoid any chance of slowdown.
DeferredUpdates::addCallableUpdate(
function () use (
$user,
$page,
$summary,
$content,
$slot
) {
$startTime = microtime( true );
$generator = $this->variableGeneratorFactory->newRunGenerator( $user, $page->getTitle() );
$vars = $generator->getStashEditVars( $content, $summary, $slot, $page );
if ( !$vars ) {
return;
}
$runner = $this->filterRunnerFactory->newRunner( $user, $page->getTitle(), $vars, 'default' );
$runner->runForStash();
$totalTime = microtime( true ) - $startTime;
$this->statsDataFactory->timing( 'timing.stashAbuseFilter', $totalTime );
},
DeferredUpdates::PRESEND
);
}
}
RecentChangeSaveHandler.php 0000666 00000001340 15133476563 0011737 0 ustar 00 <?php
// phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use MediaWiki\Extension\AbuseFilter\ChangeTags\ChangeTagger;
use MediaWiki\Hook\RecentChange_saveHook;
class RecentChangeSaveHandler implements RecentChange_saveHook {
/** @var ChangeTagger */
private $changeTagger;
/**
* @param ChangeTagger $changeTagger
*/
public function __construct( ChangeTagger $changeTagger ) {
$this->changeTagger = $changeTagger;
}
/**
* @inheritDoc
*/
public function onRecentChange_save( $recentChange ) {
$tags = $this->changeTagger->getTagsForRecentChange( $recentChange );
if ( $tags ) {
$recentChange->addTags( $tags );
}
}
}
PageSaveHandler.php 0000666 00000001156 15133476563 0010272 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use MediaWiki\Extension\AbuseFilter\EditRevUpdater;
use MediaWiki\Storage\Hook\PageSaveCompleteHook;
class PageSaveHandler implements PageSaveCompleteHook {
/** @var EditRevUpdater */
private $revUpdater;
/**
* @param EditRevUpdater $revUpdater
*/
public function __construct( EditRevUpdater $revUpdater ) {
$this->revUpdater = $revUpdater;
}
/**
* @inheritDoc
*/
public function onPageSaveComplete( $wikiPage, $user, $summary, $flags, $revisionRecord, $editResult ) {
$this->revUpdater->updateRev( $wikiPage, $revisionRecord );
}
}
UserMergeHandler.php 0000666 00000001762 15133476563 0010500 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use Config;
use MediaWiki\Extension\UserMerge\Hooks\AccountFieldsHook;
class UserMergeHandler implements AccountFieldsHook {
private Config $config;
public function __construct( Config $config ) {
$this->config = $config;
}
/**
* Tables that Extension:UserMerge needs to update
*
* @param array[] &$updateFields
*/
public function onUserMergeAccountFields( array &$updateFields ) {
$actorStage = $this->config->get( 'AbuseFilterActorTableSchemaMigrationStage' );
$updateFields[] = [
'abuse_filter',
'af_user',
'af_user_text',
'batchKey' => 'af_id',
'actorId' => 'af_actor',
'actorStage' => $actorStage,
];
$updateFields[] = [
'abuse_filter_log',
'afl_user',
'afl_user_text',
'batchKey' => 'afl_id',
];
$updateFields[] = [
'abuse_filter_history',
'afh_user',
'afh_user_text',
'batchKey' => 'afh_id',
'actorId' => 'afh_actor',
'actorStage' => $actorStage,
];
}
}
CheckUserHandler.php 0000666 00000004503 15133476563 0010452 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use MediaWiki\CheckUser\Hook\CheckUserInsertChangesRowHook;
use MediaWiki\CheckUser\Hook\CheckUserInsertLogEventRowHook;
use MediaWiki\CheckUser\Hook\CheckUserInsertPrivateEventRowHook;
use MediaWiki\Extension\AbuseFilter\FilterUser;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityUtils;
use RecentChange;
class CheckUserHandler implements
CheckUserInsertChangesRowHook,
CheckUserInsertPrivateEventRowHook,
CheckUserInsertLogEventRowHook
{
/** @var FilterUser */
private $filterUser;
/** @var UserIdentityUtils */
private $userIdentityUtils;
/**
* @param FilterUser $filterUser
* @param UserIdentityUtils $userIdentityUtils
*/
public function __construct(
FilterUser $filterUser,
UserIdentityUtils $userIdentityUtils
) {
$this->filterUser = $filterUser;
$this->userIdentityUtils = $userIdentityUtils;
}
/**
* Any edits by the filter user should always be marked as by the software
* using IP 127.0.0.1, no XFF and no UA.
*
* @inheritDoc
*/
public function onCheckUserInsertChangesRow(
string &$ip, &$xff, array &$row, UserIdentity $user, ?RecentChange $rc
) {
if (
$this->userIdentityUtils->isNamed( $user ) &&
$this->filterUser->getUserIdentity()->getId() == $user->getId()
) {
$ip = '127.0.0.1';
$xff = false;
$row['cuc_agent'] = '';
}
}
/**
* Any log actions by the filter user should always be marked as by the software
* using IP 127.0.0.1, no XFF and no UA.
*
* @inheritDoc
*/
public function onCheckUserInsertLogEventRow(
string &$ip, &$xff, array &$row, UserIdentity $user, int $id, ?RecentChange $rc
) {
if (
$this->userIdentityUtils->isNamed( $user ) &&
$this->filterUser->getUserIdentity()->getId() == $user->getId()
) {
$ip = '127.0.0.1';
$xff = false;
$row['cule_agent'] = '';
}
}
/**
* Any log actions by the filter user should always be marked as by the software
* using IP 127.0.0.1, no XFF and no UA.
*
* @inheritDoc
*/
public function onCheckUserInsertPrivateEventRow(
string &$ip, &$xff, array &$row, UserIdentity $user, ?RecentChange $rc
) {
if (
$this->userIdentityUtils->isNamed( $user ) &&
$this->filterUser->getUserIdentity()->getId() == $user->getId()
) {
$ip = '127.0.0.1';
$xff = false;
$row['cupe_agent'] = '';
}
}
}
EditPermissionHandler.php 0000666 00000006362 15133476563 0011541 0 ustar 00 <?php
// phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use JsonContent;
use MediaWiki\Content\Hook\JsonValidateSaveHook;
use MediaWiki\Extension\AbuseFilter\BlockedDomainStorage;
use MediaWiki\MediaWikiServices;
use MediaWiki\Page\PageIdentity;
use MediaWiki\Permissions\Hook\GetUserPermissionsErrorsHook;
use MediaWiki\Title\Title;
use MessageSpecifier;
use StatusValue;
use TitleValue;
use User;
/**
* This hook handler is for very simple checks, rather than the much more advanced ones
* undertaken by the FilteredActionsHandler.
*/
class EditPermissionHandler implements GetUserPermissionsErrorsHook, JsonValidateSaveHook {
/** @var string[] */
private const JSON_OBJECT_FIELDS = [
'domain',
'notes'
];
/**
* @see https://www.mediawiki.org/wiki/Manual:Hooks/getUserPermissionsErrors
*
* @param Title $title
* @param User $user
* @param string $action
* @param array|string|MessageSpecifier &$result
* @return bool|void
*/
public function onGetUserPermissionsErrors( $title, $user, $action, &$result ) {
$services = MediaWikiServices::getInstance();
// Only do anything if we're enabled on this wiki.
if ( !$services->getMainConfig()->get( 'AbuseFilterEnableBlockedExternalDomain' ) ) {
return;
}
// Ignore all actions and pages except MediaWiki: edits (and creates)
// to the page we care about
if (
!( $action == 'create' || $action == 'edit' ) ||
!$title->inNamespace( NS_MEDIAWIKI ) ||
$title->getDBkey() !== BlockedDomainStorage::TARGET_PAGE
) {
return;
}
if ( $services->getPermissionManager()->userHasRight( $user, 'editinterface' ) ) {
return;
}
// Prohibit direct actions on our page.
$result = [ 'abusefilter-blocked-domains-cannot-edit-directly', BlockedDomainStorage::TARGET_PAGE ];
return false;
}
/**
* @param JsonContent $content
* @param PageIdentity $pageIdentity
* @param StatusValue $status
* @return bool|void
*/
public function onJsonValidateSave( JsonContent $content, PageIdentity $pageIdentity, StatusValue $status ) {
$services = MediaWikiServices::getInstance();
// Only do anything if we're enabled on this wiki.
if ( !$services->getMainConfig()->get( 'AbuseFilterEnableBlockedExternalDomain' ) ) {
return;
}
$title = TitleValue::newFromPage( $pageIdentity );
if ( !$title->inNamespace( NS_MEDIAWIKI ) || $title->getText() !== BlockedDomainStorage::TARGET_PAGE ) {
return;
}
$data = $content->getData()->getValue();
if ( !is_array( $data ) ) {
$status->fatal( 'abusefilter-blocked-domains-json-error' );
return;
}
$isValid = true;
$entryNumber = 0;
foreach ( $data as $element ) {
$entryNumber++;
// Check if each element is an object with all known fields, and no other fields
if ( is_object( $element ) && count( get_object_vars( $element ) ) === count( self::JSON_OBJECT_FIELDS ) ) {
foreach ( self::JSON_OBJECT_FIELDS as $field ) {
if ( !property_exists( $element, $field ) || !is_string( $element->{$field} ) ) {
$isValid = false;
break 2;
}
}
} else {
$isValid = false;
break;
}
}
if ( !$isValid ) {
$status->fatal( 'abusefilter-blocked-domains-invalid-entry', $entryNumber );
}
}
}
RegistrationCallback.php 0000666 00000010237 15133476563 0011370 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use InvalidArgumentException;
/**
* This class runs a callback when the extension is registered, right after configuration has been
* loaded (not really a hook, but almost).
* @codeCoverageIgnore Mainly deprecation warnings and other things that can be tested by running the updater
*/
class RegistrationCallback {
public static function onRegistration(): void {
global $wgAbuseFilterProfile,
$wgAbuseFilterProfiling, $wgAbuseFilterPrivateLog, $wgAbuseFilterForceSummary,
$wgGroupPermissions, $wgAbuseFilterRestrictions, $wgAbuseFilterDisallowGlobalLocalBlocks,
$wgAbuseFilterActionRestrictions, $wgAbuseFilterLocallyDisabledGlobalActions,
$wgAbuseFilterActorTableSchemaMigrationStage;
// @todo Remove this in a future release (added in 1.33)
if ( isset( $wgAbuseFilterProfile ) || isset( $wgAbuseFilterProfiling ) ) {
wfWarn( '$wgAbuseFilterProfile and $wgAbuseFilterProfiling have been removed and ' .
'profiling is now enabled by default.' );
}
if ( isset( $wgAbuseFilterPrivateLog ) ) {
global $wgAbuseFilterLogPrivateDetailsAccess;
$wgAbuseFilterLogPrivateDetailsAccess = $wgAbuseFilterPrivateLog;
wfWarn( '$wgAbuseFilterPrivateLog has been renamed to $wgAbuseFilterLogPrivateDetailsAccess. ' .
'Please make the change in your settings; the format is identical.'
);
}
if ( isset( $wgAbuseFilterForceSummary ) ) {
global $wgAbuseFilterPrivateDetailsForceReason;
$wgAbuseFilterPrivateDetailsForceReason = $wgAbuseFilterForceSummary;
wfWarn( '$wgAbuseFilterForceSummary has been renamed to ' .
'$wgAbuseFilterPrivateDetailsForceReason. Please make the change in your settings; ' .
'the format is identical.'
);
}
$found = false;
foreach ( $wgGroupPermissions as &$perms ) {
if ( array_key_exists( 'abusefilter-private', $perms ) ) {
$perms['abusefilter-privatedetails'] = $perms[ 'abusefilter-private' ];
unset( $perms[ 'abusefilter-private' ] );
$found = true;
}
if ( array_key_exists( 'abusefilter-private-log', $perms ) ) {
$perms['abusefilter-privatedetails-log'] = $perms[ 'abusefilter-private-log' ];
unset( $perms[ 'abusefilter-private-log' ] );
$found = true;
}
}
unset( $perms );
if ( $found ) {
wfWarn( 'The group permissions "abusefilter-private-log" and "abusefilter-private" have ' .
'been renamed, respectively, to "abusefilter-privatedetails-log" and ' .
'"abusefilter-privatedetails". Please update the names in your settings.'
);
}
// @todo Remove this in a future release (added in 1.36)
if ( isset( $wgAbuseFilterDisallowGlobalLocalBlocks ) ) {
wfWarn( '$wgAbuseFilterDisallowGlobalLocalBlocks has been removed and replaced by ' .
'$wgAbuseFilterLocallyDisabledGlobalActions. You can now specify which actions to disable. ' .
'If you had set the former to true, you should set to true all of the actions in ' .
'$wgAbuseFilterRestrictions (if you were manually setting the variable) or ' .
'ConsequencesRegistry::DANGEROUS_ACTIONS. ' .
'If you had set it to false (or left the default), just remove it from your wiki settings.'
);
if ( $wgAbuseFilterDisallowGlobalLocalBlocks === true ) {
$wgAbuseFilterLocallyDisabledGlobalActions = [
'throttle' => false,
'warn' => false,
'disallow' => false,
'blockautopromote' => true,
'block' => true,
'rangeblock' => true,
'degroup' => true,
'tag' => false
];
}
}
// @todo Remove this in a future release (added in 1.36)
if ( isset( $wgAbuseFilterRestrictions ) ) {
wfWarn( '$wgAbuseFilterRestrictions has been renamed to $wgAbuseFilterActionRestrictions.' );
$wgAbuseFilterActionRestrictions = $wgAbuseFilterRestrictions;
}
// in order
$allowedStages = [
SCHEMA_COMPAT_OLD,
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
SCHEMA_COMPAT_NEW,
];
if ( !in_array( $wgAbuseFilterActorTableSchemaMigrationStage, $allowedStages ) ) {
throw new InvalidArgumentException(
'$wgAbuseFilterActorTableSchemaMigrationStage must specify a supported ' .
'combination of schema compatibility flags'
);
}
}
}
ToolLinksHandler.php 0000666 00000006240 15133476563 0010514 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use IContextSource;
use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager;
use MediaWiki\Extension\AbuseFilter\Special\SpecialAbuseLog;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Title\Title;
use SpecialPage;
use TitleValue;
use Wikimedia\IPUtils;
class ToolLinksHandler implements
\MediaWiki\Hook\ContributionsToolLinksHook,
\MediaWiki\Hook\HistoryPageToolLinksHook,
\MediaWiki\Hook\UndeletePageToolLinksHook
{
/** @var AbuseFilterPermissionManager */
private $afPermManager;
/**
* ToolLinksHandler constructor.
* @param AbuseFilterPermissionManager $afPermManager
*/
public function __construct( AbuseFilterPermissionManager $afPermManager ) {
$this->afPermManager = $afPermManager;
}
/**
* @param int $id
* @param Title $nt
* @param array &$tools
* @param SpecialPage $sp for context
*/
public function onContributionsToolLinks( $id, Title $nt, array &$tools, SpecialPage $sp ) {
$username = $nt->getText();
if ( $this->afPermManager->canViewAbuseLog( $sp->getAuthority() )
&& !IPUtils::isValidRange( $username )
) {
$linkRenderer = $sp->getLinkRenderer();
$tools['abuselog'] = $linkRenderer->makeLink(
$this->getSpecialPageTitle(),
$sp->msg( 'abusefilter-log-linkoncontribs' )->text(),
[ 'title' => $sp->msg( 'abusefilter-log-linkoncontribs-text',
$username )->text(), 'class' => 'mw-contributions-link-abuse-log' ],
[ 'wpSearchUser' => $username ]
);
}
}
/**
* @param IContextSource $context
* @param LinkRenderer $linkRenderer
* @param string[] &$links
*/
public function onHistoryPageToolLinks( IContextSource $context, LinkRenderer $linkRenderer, array &$links ) {
if ( $this->afPermManager->canViewAbuseLog( $context->getAuthority() ) ) {
$links[] = $linkRenderer->makeLink(
$this->getSpecialPageTitle(),
$context->msg( 'abusefilter-log-linkonhistory' )->text(),
[ 'title' => $context->msg( 'abusefilter-log-linkonhistory-text' )->text() ],
[ 'wpSearchTitle' => $context->getTitle()->getPrefixedText() ]
);
}
}
/**
* @param IContextSource $context
* @param LinkRenderer $linkRenderer
* @param string[] &$links
*/
public function onUndeletePageToolLinks( IContextSource $context, LinkRenderer $linkRenderer, array &$links ) {
$show = $this->afPermManager->canViewAbuseLog( $context->getAuthority() );
$action = $context->getRequest()->getVal( 'action', 'view' );
// For 'history action', the link would be added by HistoryPageToolLinks hook.
if ( $show && $action !== 'history' ) {
$links[] = $linkRenderer->makeLink(
$this->getSpecialPageTitle(),
$context->msg( 'abusefilter-log-linkonundelete' )->text(),
[ 'title' => $context->msg( 'abusefilter-log-linkonundelete-text' )->text() ],
[ 'wpSearchTitle' => $context->getTitle()->getPrefixedText() ]
);
}
}
/**
* @codeCoverageIgnore Helper for tests
* @return LinkTarget
*/
private function getSpecialPageTitle(): LinkTarget {
return defined( 'MW_PHPUNIT_TEST' )
? new TitleValue( NS_SPECIAL, SpecialAbuseLog::PAGE_NAME )
: SpecialPage::getTitleFor( SpecialAbuseLog::PAGE_NAME );
}
}
ChangeTagsHandler.php 0000666 00000001744 15133476563 0010606 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use MediaWiki\Extension\AbuseFilter\ChangeTags\ChangeTagsManager;
class ChangeTagsHandler implements
\MediaWiki\ChangeTags\Hook\ListDefinedTagsHook,
\MediaWiki\ChangeTags\Hook\ChangeTagsListActiveHook
{
/** @var ChangeTagsManager */
private $changeTagsManager;
/**
* @param ChangeTagsManager $changeTagsManager
*/
public function __construct( ChangeTagsManager $changeTagsManager ) {
$this->changeTagsManager = $changeTagsManager;
}
/**
* @param string[] &$tags
*/
public function onListDefinedTags( &$tags ) {
$tags = array_merge(
$tags,
$this->changeTagsManager->getTagsDefinedByFilters(),
[ $this->changeTagsManager->getCondsLimitTag() ]
);
}
/**
* @param string[] &$tags
*/
public function onChangeTagsListActive( &$tags ) {
$tags = array_merge(
$tags,
$this->changeTagsManager->getTagsDefinedByActiveFilters(),
[ $this->changeTagsManager->getCondsLimitTag() ]
);
}
}
SchemaChangesHandler.php 0000666 00000017567 15133476563 0011305 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use DatabaseUpdater;
use MediaWiki\Extension\AbuseFilter\Maintenance\FixOldLogEntries;
use MediaWiki\Extension\AbuseFilter\Maintenance\MigrateActorsAF;
use MediaWiki\Extension\AbuseFilter\Maintenance\NormalizeThrottleParameters;
use MediaWiki\Extension\AbuseFilter\Maintenance\UpdateVarDumps;
use MediaWiki\Installer\Hook\LoadExtensionSchemaUpdatesHook;
use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserGroupManager;
use MessageLocalizer;
use RequestContext;
use User;
class SchemaChangesHandler implements LoadExtensionSchemaUpdatesHook {
/** @var MessageLocalizer */
private $messageLocalizer;
/** @var UserGroupManager */
private $userGroupManager;
/**
* @param MessageLocalizer $messageLocalizer
* @param UserGroupManager $userGroupManager
*/
public function __construct( MessageLocalizer $messageLocalizer, UserGroupManager $userGroupManager ) {
$this->messageLocalizer = $messageLocalizer;
$this->userGroupManager = $userGroupManager;
}
/**
* @note The hook doesn't allow injecting services!
* @codeCoverageIgnore
* @return self
*/
public static function newFromGlobalState(): self {
return new self(
// @todo Use a proper MessageLocalizer once available (T247127)
RequestContext::getMain(),
MediaWikiServices::getInstance()->getUserGroupManager()
);
}
/**
* @codeCoverageIgnore This is tested by installing or updating MediaWiki
* @param DatabaseUpdater $updater
*/
public function onLoadExtensionSchemaUpdates( $updater ) {
global $wgAbuseFilterActorTableSchemaMigrationStage;
$dbType = $updater->getDB()->getType();
$dir = __DIR__ . "/../../../db_patches";
$updater->addExtensionTable(
'abuse_filter',
"$dir/$dbType/tables-generated.sql"
);
if ( $dbType === 'mysql' || $dbType === 'sqlite' ) {
$updater->dropExtensionField(
'abuse_filter_log',
'afl_log_id',
"$dir/$dbType/patch-drop_afl_log_id.sql"
);
$updater->addExtensionField(
'abuse_filter_log',
'afl_filter_id',
"$dir/$dbType/patch-split-afl_filter.sql"
);
if ( $dbType === 'mysql' ) {
$updater->renameExtensionIndex(
'abuse_filter_log',
'ip_timestamp',
'afl_ip_timestamp',
"$dir/mysql/patch-rename-indexes.sql",
true
);
// This one has its own files because apparently, sometimes this particular index can already
// have the correct name (T291725)
$updater->renameExtensionIndex(
'abuse_filter_log',
'wiki_timestamp',
'afl_wiki_timestamp',
"$dir/mysql/patch-rename-wiki-timestamp-index.sql",
true
);
// This one is also separate to avoid interferences with the afl_filter field removal below.
$updater->renameExtensionIndex(
'abuse_filter_log',
'filter_timestamp',
'afl_filter_timestamp',
"$dir/mysql/patch-rename-filter_timestamp-index.sql",
true
);
}
$updater->dropExtensionField(
'abuse_filter_log',
'afl_filter',
"$dir/$dbType/patch-remove-afl_filter.sql"
);
} elseif ( $dbType === 'postgres' ) {
$updater->addExtensionUpdate( [
'dropPgField', 'abuse_filter_log', 'afl_log_id' ] );
$updater->addExtensionUpdate( [
'setDefault', 'abuse_filter_log', 'afl_filter', ''
] );
$updater->addExtensionUpdate( [
'addPgField', 'abuse_filter_log', 'afl_global', 'SMALLINT NOT NULL DEFAULT 0'
] );
$updater->addExtensionUpdate( [
'addPgField', 'abuse_filter_log', 'afl_filter_id', 'INTEGER NOT NULL DEFAULT 0'
] );
$updater->addExtensionUpdate( [
'addPgIndex', 'abuse_filter_log', 'abuse_filter_log_filter_timestamp_full',
'(afl_global, afl_filter_id, afl_timestamp)'
] );
$updater->addExtensionUpdate( [
'dropPgIndex', 'abuse_filter_log', 'abuse_filter_log_timestamp'
] );
$updater->addExtensionUpdate( [
'dropPgField', 'abuse_filter_log', 'afl_filter'
] );
$updater->addExtensionUpdate( [
'dropDefault', 'abuse_filter_log', 'afl_filter_id'
] );
$updater->addExtensionUpdate( [
'dropDefault', 'abuse_filter_log', 'afl_global'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter', 'abuse_filter_user', 'af_user'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter', 'abuse_filter_group_enabled_id', 'af_group_enabled'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_action', 'abuse_filter_action_consequence', 'afa_consequence'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_log', 'abuse_filter_log_filter_timestamp_full', 'afl_filter_timestamp_full'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_log', 'abuse_filter_log_user_timestamp', 'afl_user_timestamp'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_log', 'abuse_filter_log_timestamp', 'afl_timestamp'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_log', 'abuse_filter_log_page_timestamp', 'afl_page_timestamp'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_log', 'abuse_filter_log_ip_timestamp', 'afl_ip_timestamp'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_log', 'abuse_filter_log_rev_id', 'afl_rev_id'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_log', 'abuse_filter_log_wiki_timestamp', 'afl_wiki_timestamp'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_history', 'abuse_filter_history_filter', 'afh_filter'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_history', 'abuse_filter_history_user', 'afh_user'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_history', 'abuse_filter_history_user_text', 'afh_user_text'
] );
$updater->addExtensionUpdate( [
'renameIndex', 'abuse_filter_history', 'abuse_filter_history_timestamp', 'afh_timestamp'
] );
$updater->addExtensionUpdate( [
'changeNullableField', ' abuse_filter_history', 'afh_public_comments', 'NULL', true
] );
$updater->addExtensionUpdate( [
'changeNullableField', ' abuse_filter_history', 'afh_actions', 'NULL', true
] );
}
$updater->addExtensionUpdate( [
'addField', 'abuse_filter', 'af_actor',
"$dir/$dbType/patch-add-af_actor.sql", true
] );
$updater->addExtensionUpdate( [
'addField', 'abuse_filter_history', 'afh_actor',
"$dir/$dbType/patch-add-afh_actor.sql", true
] );
$updater->addExtensionUpdate( [ [ $this, 'createAbuseFilterUser' ] ] );
$updater->addPostDatabaseUpdateMaintenance( NormalizeThrottleParameters::class );
$updater->addPostDatabaseUpdateMaintenance( FixOldLogEntries::class );
$updater->addPostDatabaseUpdateMaintenance( UpdateVarDumps::class );
// Don't launch the script on update.php if the migration stage is not high enough.
// This would throw an exception.
// Also check if the global is set.
// If globals aren't loaded, it's install.php, and not update.php. This is intentional,
// see for instance T193855 or T198331.
if ( isset( $wgAbuseFilterActorTableSchemaMigrationStage ) &&
( $wgAbuseFilterActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW )
) {
$updater->addPostDatabaseUpdateMaintenance( MigrateActorsAF::class );
}
}
/**
* Updater callback to create the AbuseFilter user after the user tables have been updated.
* @param DatabaseUpdater $updater
* @return bool
*/
public function createAbuseFilterUser( DatabaseUpdater $updater ): bool {
$username = $this->messageLocalizer->msg( 'abusefilter-blocker' )->inContentLanguage()->text();
$user = User::newFromName( $username );
if ( $user && !$updater->updateRowExists( 'create abusefilter-blocker-user' ) ) {
$user = User::newSystemUser( $username, [ 'steal' => true ] );
$updater->insertUpdateRow( 'create abusefilter-blocker-user' );
// Promote user so it doesn't look too crazy.
$this->userGroupManager->addUserToGroup( $user, 'sysop' );
return true;
}
return false;
}
}
AutoPromoteGroupsHandler.php 0000666 00000004203 15133476563 0012251 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use BagOStuff;
use MediaWiki\Extension\AbuseFilter\BlockAutopromoteStore;
use MediaWiki\Extension\AbuseFilter\Consequences\ConsequencesRegistry;
use MediaWiki\User\Hook\GetAutoPromoteGroupsHook;
use MediaWiki\User\UserIdentity;
use ObjectCache;
class AutoPromoteGroupsHandler implements GetAutoPromoteGroupsHook {
/** @var BagOStuff */
private $cache;
/** @var ConsequencesRegistry */
private $consequencesRegistry;
/** @var BlockAutopromoteStore */
private $blockAutopromoteStore;
/**
* @param BagOStuff $cache
* @param ConsequencesRegistry $consequencesRegistry
* @param BlockAutopromoteStore $blockAutopromoteStore
*/
public function __construct(
BagOStuff $cache,
ConsequencesRegistry $consequencesRegistry,
BlockAutopromoteStore $blockAutopromoteStore
) {
$this->cache = $cache;
$this->consequencesRegistry = $consequencesRegistry;
$this->blockAutopromoteStore = $blockAutopromoteStore;
}
/**
* @param ConsequencesRegistry $consequencesRegistry
* @param BlockAutopromoteStore $blockAutopromoteStore
* @return self
* @todo Can we avoid this factory method?
*/
public static function factory(
ConsequencesRegistry $consequencesRegistry,
BlockAutopromoteStore $blockAutopromoteStore
): self {
return new self(
ObjectCache::getInstance( 'hash' ),
$consequencesRegistry,
$blockAutopromoteStore
);
}
/**
* @param UserIdentity $user
* @param string[] &$promote
*/
public function onGetAutoPromoteGroups( $user, &$promote ): void {
if (
in_array( 'blockautopromote', $this->consequencesRegistry->getAllEnabledActionNames() )
&& $promote
) {
// Proxy the blockautopromote data to a faster backend, using an appropriate key
$quickCacheKey = $this->cache->makeKey(
'abusefilter',
'blockautopromote',
'quick',
$user->getId()
);
$blocked = (bool)$this->cache->getWithSetCallback(
$quickCacheKey,
BagOStuff::TTL_PROC_LONG,
function () use ( $user ) {
return $this->blockAutopromoteStore->getAutoPromoteBlockStatus( $user );
}
);
if ( $blocked ) {
$promote = [];
}
}
}
}
EchoHandler.php 0000666 00000001646 15133476563 0007461 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use EchoAttributeManager;
use EchoUserLocator;
use MediaWiki\Extension\AbuseFilter\EchoNotifier;
use MediaWiki\Extension\AbuseFilter\ThrottleFilterPresentationModel;
use MediaWiki\Extension\Notifications\Hooks\BeforeCreateEchoEventHook;
class EchoHandler implements BeforeCreateEchoEventHook {
/**
* @param array &$notifications
* @param array &$notificationCategories
* @param array &$icons
*/
public function onBeforeCreateEchoEvent(
array &$notifications,
array &$notificationCategories,
array &$icons
) {
$notifications[ EchoNotifier::EVENT_TYPE ] = [
'category' => 'system',
'section' => 'alert',
'group' => 'negative',
'presentation-model' => ThrottleFilterPresentationModel::class,
EchoAttributeManager::ATTR_LOCATORS => [
[
[ EchoUserLocator::class, 'locateFromEventExtra' ],
[ 'user' ]
]
],
];
}
}
UserRenameHandler.php 0000666 00000001606 15133476563 0010645 0 ustar 00 <?php
namespace MediaWiki\Extension\AbuseFilter\Hooks\Handlers;
use MediaWiki\RenameUser\Hook\RenameUserSQLHook;
use MediaWiki\RenameUser\RenameuserSQL;
class UserRenameHandler implements RenameUserSQLHook {
/**
* @inheritDoc
*/
public function onRenameUserSQL( RenameuserSQL $renameUserSql ): void {
global $wgAbuseFilterActorTableSchemaMigrationStage;
if ( !( $wgAbuseFilterActorTableSchemaMigrationStage & SCHEMA_COMPAT_OLD ) ) {
return;
}
$renameUserSql->tablesJob['abuse_filter'] = [
RenameuserSQL::NAME_COL => 'af_user_text',
RenameuserSQL::UID_COL => 'af_user',
RenameuserSQL::TIME_COL => 'af_timestamp',
'uniqueKey' => 'af_id'
];
$renameUserSql->tablesJob['abuse_filter_history'] = [
RenameuserSQL::NAME_COL => 'afh_user_text',
RenameuserSQL::UID_COL => 'afh_user',
RenameuserSQL::TIME_COL => 'afh_timestamp',
'uniqueKey' => 'afh_id'
];
}
}
Back to Directory
File Manager