Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
66.67% |
64 / 96 |
|
50.00% |
1 / 2 |
CRAP | |
0.00% |
0 / 1 |
| QueryAbuseFilters | |
66.67% |
64 / 96 |
|
50.00% |
1 / 2 |
80.37 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| execute | |
66.32% |
63 / 95 |
|
0.00% |
0 / 1 |
71.14 | |||
| getAllowedParams | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
| getExamplesMessages | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
| 1 | <?php |
| 2 | /** |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License as published by |
| 5 | * the Free Software Foundation; either version 2 of the License, or |
| 6 | * (at your option) any later version. |
| 7 | * |
| 8 | * This program is distributed in the hope that it will be useful, |
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
| 12 | * |
| 13 | * You should have received a copy of the GNU General Public License along |
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., |
| 15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 16 | * http://www.gnu.org/copyleft/gpl.html |
| 17 | */ |
| 18 | |
| 19 | namespace MediaWiki\Extension\AbuseFilter\Api; |
| 20 | |
| 21 | use MediaWiki\Api\ApiBase; |
| 22 | use MediaWiki\Api\ApiQuery; |
| 23 | use MediaWiki\Api\ApiQueryBase; |
| 24 | use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager; |
| 25 | use MediaWiki\Extension\AbuseFilter\Filter\Flags; |
| 26 | use MediaWiki\Extension\AbuseFilter\FilterLookup; |
| 27 | use Wikimedia\ParamValidator\ParamValidator; |
| 28 | use Wikimedia\ParamValidator\TypeDef\IntegerDef; |
| 29 | use Wikimedia\Timestamp\ConvertibleTimestamp; |
| 30 | |
| 31 | /** |
| 32 | * Query module to list abuse filter details. |
| 33 | * |
| 34 | * @copyright 2009 Alex Z. <mrzmanwiki AT gmail DOT com> |
| 35 | * Based mostly on code by Bryan Tong Minh and Roan Kattouw |
| 36 | * |
| 37 | * @ingroup API |
| 38 | * @ingroup Extensions |
| 39 | */ |
| 40 | class QueryAbuseFilters extends ApiQueryBase { |
| 41 | |
| 42 | public function __construct( |
| 43 | ApiQuery $query, |
| 44 | string $moduleName, |
| 45 | private readonly AbuseFilterPermissionManager $afPermManager, |
| 46 | private readonly FilterLookup $filterLookup |
| 47 | ) { |
| 48 | parent::__construct( $query, $moduleName, 'abf' ); |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * @inheritDoc |
| 53 | */ |
| 54 | public function execute() { |
| 55 | $this->checkUserRightsAny( 'abusefilter-view' ); |
| 56 | |
| 57 | $params = $this->extractRequestParams(); |
| 58 | |
| 59 | $prop = array_fill_keys( $params['prop'], true ); |
| 60 | $fld_id = isset( $prop['id'] ); |
| 61 | $fld_desc = isset( $prop['description'] ); |
| 62 | $fld_pattern = isset( $prop['pattern'] ); |
| 63 | $fld_actions = isset( $prop['actions'] ); |
| 64 | $fld_hits = isset( $prop['hits'] ); |
| 65 | $fld_comments = isset( $prop['comments'] ); |
| 66 | $fld_user = isset( $prop['lasteditor'] ); |
| 67 | $fld_time = isset( $prop['lastedittime'] ); |
| 68 | $fld_status = isset( $prop['status'] ); |
| 69 | $fld_private = isset( $prop['private'] ); |
| 70 | $fld_protected = isset( $prop['protected'] ); |
| 71 | |
| 72 | $result = $this->getResult(); |
| 73 | |
| 74 | // Use the SelectQueryBuilder from the FilterLookup service as a base so that we can construct |
| 75 | // Filter objects from the rows got in the query. |
| 76 | $this->getQueryBuilder()->queryInfo( |
| 77 | $this->filterLookup->getAbuseFilterQueryBuilder( $this->getDB() )->getQueryInfo() |
| 78 | ); |
| 79 | |
| 80 | $this->addOption( 'LIMIT', $params['limit'] + 1 ); |
| 81 | |
| 82 | $this->addWhereRange( 'af_id', $params['dir'], $params['startid'], $params['endid'] ); |
| 83 | |
| 84 | if ( $params['show'] !== null ) { |
| 85 | $show = array_fill_keys( $params['show'], true ); |
| 86 | |
| 87 | /* Check for conflicting parameters. */ |
| 88 | if ( ( isset( $show['enabled'] ) && isset( $show['!enabled'] ) ) |
| 89 | || ( isset( $show['deleted'] ) && isset( $show['!deleted'] ) ) |
| 90 | || ( isset( $show['private'] ) && isset( $show['!private'] ) ) |
| 91 | ) { |
| 92 | $this->dieWithError( 'apierror-show' ); |
| 93 | } |
| 94 | |
| 95 | $dbr = $this->getDb(); |
| 96 | $this->addWhereIf( $dbr->expr( 'af_enabled', '=', 0 ), isset( $show['!enabled'] ) ); |
| 97 | $this->addWhereIf( $dbr->expr( 'af_enabled', '!=', 0 ), isset( $show['enabled'] ) ); |
| 98 | $this->addWhereIf( $dbr->expr( 'af_deleted', '=', 0 ), isset( $show['!deleted'] ) ); |
| 99 | $this->addWhereIf( $dbr->expr( 'af_deleted', '!=', 0 ), isset( $show['deleted'] ) ); |
| 100 | $this->addWhereIf( |
| 101 | $dbr->bitAnd( 'af_hidden', Flags::FILTER_HIDDEN ) . ' = 0', |
| 102 | isset( $show['!private'] ) |
| 103 | ); |
| 104 | $this->addWhereIf( |
| 105 | $dbr->bitAnd( 'af_hidden', Flags::FILTER_HIDDEN ) . ' != 0', |
| 106 | isset( $show['private'] ) |
| 107 | ); |
| 108 | $this->addWhereIf( |
| 109 | $dbr->bitAnd( 'af_hidden', Flags::FILTER_USES_PROTECTED_VARS ) . ' != 0', |
| 110 | isset( $show['!protected'] ) |
| 111 | ); |
| 112 | $this->addWhereIf( |
| 113 | $dbr->bitAnd( 'af_hidden', Flags::FILTER_USES_PROTECTED_VARS ) . ' = 0', |
| 114 | isset( $show['!protected'] ) |
| 115 | ); |
| 116 | } |
| 117 | |
| 118 | $res = $this->select( __METHOD__ ); |
| 119 | |
| 120 | $showhidden = $this->afPermManager->canViewPrivateFilters( $this->getAuthority() ); |
| 121 | |
| 122 | $count = 0; |
| 123 | foreach ( $res as $row ) { |
| 124 | // FilterLookup::filterFromRow will override af_actions, so we need to define the callback to generate |
| 125 | // the data. We do not need to define anything other than the names because we only call |
| 126 | // AbstractFilter::getActionNames. |
| 127 | $actions = array_flip( explode( ',', $row->af_actions ) ); |
| 128 | $filter = $this->filterLookup->filterFromRow( $row, $actions ); |
| 129 | if ( ++$count > $params['limit'] ) { |
| 130 | // We've had enough |
| 131 | $this->setContinueEnumParameter( 'startid', $filter->getID() ); |
| 132 | break; |
| 133 | } |
| 134 | |
| 135 | // Hide the pattern and non-public comments from the API response if the user would not |
| 136 | // be able to open the editor for the filter. |
| 137 | $canViewExtendedDetailsAboutFilter = ( !$filter->isHidden() || $showhidden ); |
| 138 | |
| 139 | if ( $filter->isProtected() && $canViewExtendedDetailsAboutFilter ) { |
| 140 | $canViewExtendedDetailsAboutFilter = $this->afPermManager |
| 141 | ->canViewProtectedVariablesInFilter( $this->getAuthority(), $filter ) |
| 142 | ->isGood(); |
| 143 | } |
| 144 | |
| 145 | $entry = []; |
| 146 | if ( $fld_id ) { |
| 147 | $entry['id'] = $filter->getID(); |
| 148 | } |
| 149 | if ( $fld_desc ) { |
| 150 | $entry['description'] = $filter->getName(); |
| 151 | } |
| 152 | if ( $fld_pattern && $canViewExtendedDetailsAboutFilter ) { |
| 153 | $entry['pattern'] = $filter->getRules(); |
| 154 | } |
| 155 | if ( $fld_actions ) { |
| 156 | $entry['actions'] = implode( ',', $filter->getActionsNames() ); |
| 157 | } |
| 158 | if ( $fld_hits ) { |
| 159 | $entry['hits'] = $filter->getHitCount(); |
| 160 | } |
| 161 | if ( $fld_comments && $canViewExtendedDetailsAboutFilter ) { |
| 162 | $entry['comments'] = $filter->getComments(); |
| 163 | } |
| 164 | if ( $fld_user ) { |
| 165 | $entry['lasteditor'] = $filter->getLastEditInfo()->getUserName(); |
| 166 | } |
| 167 | if ( $fld_time ) { |
| 168 | $entry['lastedittime'] = ConvertibleTimestamp::convert( |
| 169 | TS_ISO_8601, $filter->getLastEditInfo()->getTimestamp() |
| 170 | ); |
| 171 | } |
| 172 | if ( $fld_private && $filter->isHidden() ) { |
| 173 | $entry['private'] = ''; |
| 174 | } |
| 175 | if ( $fld_protected && $filter->isProtected() ) { |
| 176 | $entry['protected'] = ''; |
| 177 | } |
| 178 | if ( $fld_status ) { |
| 179 | if ( $filter->isEnabled() ) { |
| 180 | $entry['enabled'] = ''; |
| 181 | } |
| 182 | if ( $filter->isDeleted() ) { |
| 183 | $entry['deleted'] = ''; |
| 184 | } |
| 185 | } |
| 186 | if ( $entry ) { |
| 187 | $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $entry ); |
| 188 | if ( !$fit ) { |
| 189 | $this->setContinueEnumParameter( 'startid', $filter->getID() ); |
| 190 | break; |
| 191 | } |
| 192 | } |
| 193 | } |
| 194 | $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'filter' ); |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | * @codeCoverageIgnore Merely declarative |
| 199 | * @inheritDoc |
| 200 | */ |
| 201 | public function getAllowedParams() { |
| 202 | return [ |
| 203 | 'startid' => [ |
| 204 | ParamValidator::PARAM_TYPE => 'integer' |
| 205 | ], |
| 206 | 'endid' => [ |
| 207 | ParamValidator::PARAM_TYPE => 'integer', |
| 208 | ], |
| 209 | 'dir' => [ |
| 210 | ParamValidator::PARAM_TYPE => [ |
| 211 | 'older', |
| 212 | 'newer' |
| 213 | ], |
| 214 | ParamValidator::PARAM_DEFAULT => 'newer', |
| 215 | ApiBase::PARAM_HELP_MSG => 'api-help-param-direction', |
| 216 | ], |
| 217 | 'show' => [ |
| 218 | ParamValidator::PARAM_ISMULTI => true, |
| 219 | ParamValidator::PARAM_TYPE => [ |
| 220 | 'enabled', |
| 221 | '!enabled', |
| 222 | 'deleted', |
| 223 | '!deleted', |
| 224 | 'private', |
| 225 | '!private', |
| 226 | 'protected', |
| 227 | '!protected', |
| 228 | ], |
| 229 | ], |
| 230 | 'limit' => [ |
| 231 | ParamValidator::PARAM_DEFAULT => 10, |
| 232 | ParamValidator::PARAM_TYPE => 'limit', |
| 233 | IntegerDef::PARAM_MIN => 1, |
| 234 | IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1, |
| 235 | IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2 |
| 236 | ], |
| 237 | 'prop' => [ |
| 238 | ParamValidator::PARAM_DEFAULT => 'id|description|actions|status', |
| 239 | ParamValidator::PARAM_TYPE => [ |
| 240 | 'id', |
| 241 | 'description', |
| 242 | 'pattern', |
| 243 | 'actions', |
| 244 | 'hits', |
| 245 | 'comments', |
| 246 | 'lasteditor', |
| 247 | 'lastedittime', |
| 248 | 'status', |
| 249 | 'private', |
| 250 | 'protected', |
| 251 | ], |
| 252 | ParamValidator::PARAM_ISMULTI => true |
| 253 | ] |
| 254 | ]; |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * @codeCoverageIgnore Merely declarative |
| 259 | * @inheritDoc |
| 260 | */ |
| 261 | protected function getExamplesMessages() { |
| 262 | return [ |
| 263 | 'action=query&list=abusefilters&abfshow=enabled|!private' |
| 264 | => 'apihelp-query+abusefilters-example-1', |
| 265 | 'action=query&list=abusefilters&abfprop=id|description|pattern' |
| 266 | => 'apihelp-query+abusefilters-example-2', |
| 267 | ]; |
| 268 | } |
| 269 | } |