Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
81.97% |
100 / 122 |
|
60.00% |
3 / 5 |
CRAP | |
0.00% |
0 / 1 |
ApiQueryCheckUserLog | |
81.97% |
100 / 122 |
|
60.00% |
3 / 5 |
27.38 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
79.49% |
62 / 78 |
|
0.00% |
0 / 1 |
23.45 | |||
getAllowedParams | |
100.00% |
33 / 33 |
|
100.00% |
1 / 1 |
1 | |||
getExamplesMessages | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
getHelpUrls | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace MediaWiki\CheckUser\Api; |
4 | |
5 | use ApiBase; |
6 | use ApiQuery; |
7 | use ApiQueryBase; |
8 | use MediaWiki\CheckUser\Services\CheckUserLogService; |
9 | use MediaWiki\CommentStore\CommentStore; |
10 | use MediaWiki\User\UserFactory; |
11 | use Wikimedia\IPUtils; |
12 | use Wikimedia\ParamValidator\ParamValidator; |
13 | use Wikimedia\ParamValidator\TypeDef\IntegerDef; |
14 | |
15 | /** |
16 | * CheckUser API Query Module |
17 | */ |
18 | class ApiQueryCheckUserLog extends ApiQueryBase { |
19 | private CommentStore $commentStore; |
20 | private CheckUserLogService $checkUserLogService; |
21 | private UserFactory $userFactory; |
22 | |
23 | /** |
24 | * @param ApiQuery $query |
25 | * @param string $moduleName |
26 | * @param CommentStore $commentStore |
27 | * @param CheckUserLogService $checkUserLogService |
28 | * @param UserFactory $userFactory |
29 | */ |
30 | public function __construct( |
31 | $query, $moduleName, |
32 | CommentStore $commentStore, |
33 | CheckUserLogService $checkUserLogService, |
34 | UserFactory $userFactory |
35 | ) { |
36 | parent::__construct( $query, $moduleName, 'cul' ); |
37 | $this->commentStore = $commentStore; |
38 | $this->checkUserLogService = $checkUserLogService; |
39 | $this->userFactory = $userFactory; |
40 | } |
41 | |
42 | public function execute() { |
43 | $db = $this->getDB(); |
44 | $params = $this->extractRequestParams(); |
45 | $this->checkUserRightsAny( 'checkuser-log' ); |
46 | |
47 | $limit = $params['limit']; |
48 | $continue = $params['continue']; |
49 | $dir = $params['dir']; |
50 | |
51 | $this->addTables( [ 'cu_log', 'actor' ] ); |
52 | $this->addOption( 'LIMIT', $limit + 1 ); |
53 | $this->addTimestampWhereRange( 'cul_timestamp', $dir, $params['from'], $params['to'] ); |
54 | $fields = [ |
55 | 'cul_id', 'cul_timestamp', 'cul_type', 'cul_target_text', 'actor_name' |
56 | ]; |
57 | $this->addJoinConds( [ 'actor' => [ 'JOIN', 'actor_id=cul_actor' ] ] ); |
58 | |
59 | $reasonCommentQuery = $this->commentStore->getJoin( 'cul_reason' ); |
60 | $this->addTables( $reasonCommentQuery['tables'] ); |
61 | $this->addJoinConds( $reasonCommentQuery['joins'] ); |
62 | $fields += $reasonCommentQuery['fields']; |
63 | |
64 | if ( isset( $params['reason'] ) ) { |
65 | $plaintextReasonCommentQuery = $this->commentStore->getJoin( 'cul_reason_plaintext' ); |
66 | $this->addTables( $plaintextReasonCommentQuery['tables'] ); |
67 | $this->addJoinConds( $plaintextReasonCommentQuery['joins'] ); |
68 | $fields += $plaintextReasonCommentQuery['fields']; |
69 | } |
70 | |
71 | $this->addFields( $fields ); |
72 | |
73 | // Order by both timestamp and id |
74 | $order = ( $dir === 'newer' ? '' : ' DESC' ); |
75 | $this->addOption( 'ORDER BY', [ 'cul_timestamp' . $order, 'cul_id' . $order ] ); |
76 | |
77 | if ( isset( $params['user'] ) ) { |
78 | $this->addWhereFld( 'actor_name', $params['user'] ); |
79 | } |
80 | if ( isset( $params['target'] ) ) { |
81 | $cond = $this->checkUserLogService->getTargetSearchConds( $params['target'] ); |
82 | if ( !$cond ) { |
83 | if ( IPUtils::isIPAddress( $params['target'] ) ) { |
84 | $this->dieWithError( 'apierror-badip', 'invalidip' ); |
85 | } else { |
86 | $this->dieWithError( 'apierror-checkuser-nosuchuser', 'nosuchuser' ); |
87 | } |
88 | } |
89 | $this->addWhere( $cond ); |
90 | if ( IPUtils::isIPAddress( $params['target'] ) ) { |
91 | // Use the cul_target_hex index on the query if the target is an IP |
92 | // otherwise the query could take a long time (T342639) |
93 | $this->addOption( 'USE INDEX', [ 'cu_log' => 'cul_target_hex' ] ); |
94 | } |
95 | } |
96 | |
97 | if ( isset( $params['reason'] ) ) { |
98 | $plaintextReason = $this->checkUserLogService->getPlaintextReason( $params['reason'] ); |
99 | $this->addWhereFld( |
100 | 'comment_cul_reason_plaintext.comment_text', |
101 | $plaintextReason |
102 | ); |
103 | } |
104 | |
105 | if ( $continue !== null ) { |
106 | $cont = $this->parseContinueParamOrDie( $continue, [ 'timestamp', 'int' ] ); |
107 | $op = $dir === 'older' ? '<=' : '>='; |
108 | $this->addWhere( $db->buildComparison( $op, [ |
109 | 'cul_timestamp' => $db->timestamp( $cont[0] ), |
110 | 'cul_id' => $cont[1], |
111 | ] ) ); |
112 | } |
113 | |
114 | $res = $this->select( __METHOD__ ); |
115 | $result = $this->getResult(); |
116 | |
117 | $count = 0; |
118 | foreach ( $res as $row ) { |
119 | if ( ++$count > $limit ) { |
120 | $this->setContinueEnumParameter( 'continue', "$row->cul_timestamp|$row->cul_id" ); |
121 | break; |
122 | } |
123 | $log = [ |
124 | 'timestamp' => wfTimestamp( TS_ISO_8601, $row->cul_timestamp ), |
125 | 'checkuser' => $row->actor_name, |
126 | 'type' => $row->cul_type, |
127 | 'reason' => $this->commentStore->getComment( 'cul_reason', $row )->text, |
128 | 'target' => $row->cul_target_text, |
129 | ]; |
130 | |
131 | $checkUser = $this->userFactory->newFromName( $row->actor_name ); |
132 | if ( |
133 | $checkUser && |
134 | $checkUser->isHidden() && |
135 | !$this->getAuthority()->isAllowed( 'hideuser' ) |
136 | ) { |
137 | $log['checkuser'] = $this->msg( 'rev-deleted-user' )->plain(); |
138 | } |
139 | |
140 | $targetUser = $this->userFactory->newFromName( $row->cul_target_text ); |
141 | if ( |
142 | $targetUser && |
143 | $targetUser->isHidden() && |
144 | !$this->getAuthority()->isAllowed( 'hideuser' ) |
145 | ) { |
146 | $log['target'] = $this->msg( 'rev-deleted-user' )->plain(); |
147 | } |
148 | $fit = $result->addValue( [ 'query', $this->getModuleName(), 'entries' ], null, $log ); |
149 | if ( !$fit ) { |
150 | $this->setContinueEnumParameter( 'continue', "$row->cul_timestamp|$row->cul_id" ); |
151 | break; |
152 | } |
153 | } |
154 | |
155 | $result->addIndexedTagName( [ 'query', $this->getModuleName(), 'entries' ], 'entry' ); |
156 | } |
157 | |
158 | /** @inheritDoc */ |
159 | public function getAllowedParams() { |
160 | return [ |
161 | 'user' => null, |
162 | 'target' => null, |
163 | 'reason' => null, |
164 | 'limit' => [ |
165 | ParamValidator::PARAM_DEFAULT => 10, |
166 | ParamValidator::PARAM_TYPE => 'limit', |
167 | IntegerDef::PARAM_MIN => 1, |
168 | IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1, |
169 | IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2, |
170 | ], |
171 | 'dir' => [ |
172 | ParamValidator::PARAM_DEFAULT => 'older', |
173 | ParamValidator::PARAM_TYPE => [ |
174 | 'newer', |
175 | 'older' |
176 | ], |
177 | ApiBase::PARAM_HELP_MSG => 'checkuser-api-help-param-direction', |
178 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [ |
179 | 'newer' => 'checkuser-api-help-paramvalue-direction-newer', |
180 | 'older' => 'checkuser-api-help-paramvalue-direction-older', |
181 | ], |
182 | ], |
183 | 'from' => [ |
184 | ParamValidator::PARAM_TYPE => 'timestamp', |
185 | ], |
186 | 'to' => [ |
187 | ParamValidator::PARAM_TYPE => 'timestamp', |
188 | ], |
189 | 'continue' => [ |
190 | ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', |
191 | ], |
192 | ]; |
193 | } |
194 | |
195 | /** @inheritDoc */ |
196 | protected function getExamplesMessages() { |
197 | return [ |
198 | 'action=query&list=checkuserlog&culuser=Example&cullimit=25' |
199 | => 'apihelp-query+checkuserlog-example-1', |
200 | 'action=query&list=checkuserlog&cultarget=192.0.2.0/24&culfrom=2011-10-15T23:00:00Z' |
201 | => 'apihelp-query+checkuserlog-example-2', |
202 | ]; |
203 | } |
204 | |
205 | /** |
206 | * @inheritDoc |
207 | */ |
208 | public function getHelpUrls() { |
209 | return 'https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:CheckUser#API'; |
210 | } |
211 | } |