This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * |
||
4 | * |
||
5 | * Created on July 30, 2007 |
||
6 | * |
||
7 | * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com" |
||
8 | * |
||
9 | * This program is free software; you can redistribute it and/or modify |
||
10 | * it under the terms of the GNU General Public License as published by |
||
11 | * the Free Software Foundation; either version 2 of the License, or |
||
12 | * (at your option) any later version. |
||
13 | * |
||
14 | * This program is distributed in the hope that it will be useful, |
||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
17 | * GNU General Public License for more details. |
||
18 | * |
||
19 | * You should have received a copy of the GNU General Public License along |
||
20 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
22 | * http://www.gnu.org/copyleft/gpl.html |
||
23 | * |
||
24 | * @file |
||
25 | */ |
||
26 | |||
27 | /** |
||
28 | * Query module to get information about a list of users |
||
29 | * |
||
30 | * @ingroup API |
||
31 | */ |
||
32 | class ApiQueryUsers extends ApiQueryBase { |
||
33 | |||
34 | private $tokenFunctions, $prop; |
||
0 ignored issues
–
show
|
|||
35 | |||
36 | /** |
||
37 | * Properties whose contents does not depend on who is looking at them. If the usprops field |
||
38 | * contains anything not listed here, the cache mode will never be public for logged-in users. |
||
39 | * @var array |
||
40 | */ |
||
41 | protected static $publicProps = [ |
||
42 | // everything except 'blockinfo' which might show hidden records if the user |
||
43 | // making the request has the appropriate permissions |
||
44 | 'groups', |
||
45 | 'implicitgroups', |
||
46 | 'rights', |
||
47 | 'editcount', |
||
48 | 'registration', |
||
49 | 'emailable', |
||
50 | 'gender', |
||
51 | 'centralids', |
||
52 | 'cancreate', |
||
53 | ]; |
||
54 | |||
55 | public function __construct( ApiQuery $query, $moduleName ) { |
||
56 | parent::__construct( $query, $moduleName, 'us' ); |
||
57 | } |
||
58 | |||
59 | /** |
||
60 | * Get an array mapping token names to their handler functions. |
||
61 | * The prototype for a token function is func($user) |
||
62 | * it should return a token or false (permission denied) |
||
63 | * @deprecated since 1.24 |
||
64 | * @return array Array of tokenname => function |
||
65 | */ |
||
66 | View Code Duplication | protected function getTokenFunctions() { |
|
67 | // Don't call the hooks twice |
||
68 | if ( isset( $this->tokenFunctions ) ) { |
||
69 | return $this->tokenFunctions; |
||
70 | } |
||
71 | |||
72 | // If we're in a mode that breaks the same-origin policy, no tokens can |
||
73 | // be obtained |
||
74 | if ( $this->lacksSameOriginSecurity() ) { |
||
75 | return []; |
||
76 | } |
||
77 | |||
78 | $this->tokenFunctions = [ |
||
79 | 'userrights' => [ 'ApiQueryUsers', 'getUserrightsToken' ], |
||
80 | ]; |
||
81 | Hooks::run( 'APIQueryUsersTokens', [ &$this->tokenFunctions ] ); |
||
82 | |||
83 | return $this->tokenFunctions; |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * @deprecated since 1.24 |
||
88 | * @param User $user |
||
89 | * @return string |
||
90 | */ |
||
91 | public static function getUserrightsToken( $user ) { |
||
92 | global $wgUser; |
||
93 | |||
94 | // Since the permissions check for userrights is non-trivial, |
||
95 | // don't bother with it here |
||
96 | return $wgUser->getEditToken( $user->getName() ); |
||
97 | } |
||
98 | |||
99 | public function execute() { |
||
100 | $params = $this->extractRequestParams(); |
||
101 | |||
102 | View Code Duplication | if ( !is_null( $params['prop'] ) ) { |
|
103 | $this->prop = array_flip( $params['prop'] ); |
||
104 | } else { |
||
105 | $this->prop = []; |
||
106 | } |
||
107 | |||
108 | $users = (array)$params['users']; |
||
109 | $goodNames = $done = []; |
||
110 | $result = $this->getResult(); |
||
111 | // Canonicalize user names |
||
112 | foreach ( $users as $u ) { |
||
113 | $n = User::getCanonicalName( $u ); |
||
114 | if ( $n === false || $n === '' ) { |
||
115 | $vals = [ 'name' => $u, 'invalid' => true ]; |
||
116 | $fit = $result->addValue( [ 'query', $this->getModuleName() ], |
||
117 | null, $vals ); |
||
118 | if ( !$fit ) { |
||
119 | $this->setContinueEnumParameter( 'users', |
||
120 | implode( '|', array_diff( $users, $done ) ) ); |
||
121 | $goodNames = []; |
||
122 | break; |
||
123 | } |
||
124 | $done[] = $u; |
||
125 | } else { |
||
126 | $goodNames[] = $n; |
||
127 | } |
||
128 | } |
||
129 | |||
130 | $result = $this->getResult(); |
||
131 | |||
132 | if ( count( $goodNames ) ) { |
||
133 | $this->addTables( 'user' ); |
||
134 | $this->addFields( User::selectFields() ); |
||
135 | $this->addWhereFld( 'user_name', $goodNames ); |
||
136 | |||
137 | $this->showHiddenUsersAddBlockInfo( isset( $this->prop['blockinfo'] ) ); |
||
138 | |||
139 | $data = []; |
||
140 | $res = $this->select( __METHOD__ ); |
||
141 | $this->resetQueryParams(); |
||
142 | |||
143 | // get user groups if needed |
||
144 | if ( isset( $this->prop['groups'] ) || isset( $this->prop['rights'] ) ) { |
||
145 | $userGroups = []; |
||
146 | |||
147 | $this->addTables( 'user' ); |
||
148 | $this->addWhereFld( 'user_name', $goodNames ); |
||
149 | $this->addTables( 'user_groups' ); |
||
150 | $this->addJoinConds( [ 'user_groups' => [ 'INNER JOIN', 'ug_user=user_id' ] ] ); |
||
151 | $this->addFields( [ 'user_name', 'ug_group' ] ); |
||
152 | $userGroupsRes = $this->select( __METHOD__ ); |
||
153 | |||
154 | foreach ( $userGroupsRes as $row ) { |
||
155 | $userGroups[$row->user_name][] = $row->ug_group; |
||
156 | } |
||
157 | } |
||
158 | |||
159 | foreach ( $res as $row ) { |
||
160 | // create user object and pass along $userGroups if set |
||
161 | // that reduces the number of database queries needed in User dramatically |
||
162 | if ( !isset( $userGroups ) ) { |
||
163 | $user = User::newFromRow( $row ); |
||
0 ignored issues
–
show
It seems like
$row defined by $row on line 159 can be null ; however, User::newFromRow() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
164 | } else { |
||
165 | if ( !isset( $userGroups[$row->user_name] ) || !is_array( $userGroups[$row->user_name] ) ) { |
||
166 | $userGroups[$row->user_name] = []; |
||
167 | } |
||
168 | $user = User::newFromRow( $row, [ 'user_groups' => $userGroups[$row->user_name] ] ); |
||
169 | } |
||
170 | $name = $user->getName(); |
||
171 | |||
172 | $data[$name]['userid'] = $user->getId(); |
||
173 | $data[$name]['name'] = $name; |
||
174 | |||
175 | if ( isset( $this->prop['editcount'] ) ) { |
||
176 | $data[$name]['editcount'] = $user->getEditCount(); |
||
177 | } |
||
178 | |||
179 | if ( isset( $this->prop['registration'] ) ) { |
||
180 | $data[$name]['registration'] = wfTimestampOrNull( TS_ISO_8601, $user->getRegistration() ); |
||
0 ignored issues
–
show
|
|||
181 | } |
||
182 | |||
183 | if ( isset( $this->prop['groups'] ) ) { |
||
184 | $data[$name]['groups'] = $user->getEffectiveGroups(); |
||
185 | } |
||
186 | |||
187 | if ( isset( $this->prop['implicitgroups'] ) ) { |
||
188 | $data[$name]['implicitgroups'] = $user->getAutomaticGroups(); |
||
189 | } |
||
190 | |||
191 | if ( isset( $this->prop['rights'] ) ) { |
||
192 | $data[$name]['rights'] = $user->getRights(); |
||
193 | } |
||
194 | if ( $row->ipb_deleted ) { |
||
195 | $data[$name]['hidden'] = true; |
||
196 | } |
||
197 | if ( isset( $this->prop['blockinfo'] ) && !is_null( $row->ipb_by_text ) ) { |
||
198 | $data[$name]['blockid'] = (int)$row->ipb_id; |
||
199 | $data[$name]['blockedby'] = $row->ipb_by_text; |
||
200 | $data[$name]['blockedbyid'] = (int)$row->ipb_by; |
||
201 | $data[$name]['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $row->ipb_timestamp ); |
||
202 | $data[$name]['blockreason'] = $row->ipb_reason; |
||
203 | $data[$name]['blockexpiry'] = $row->ipb_expiry; |
||
204 | } |
||
205 | |||
206 | if ( isset( $this->prop['emailable'] ) ) { |
||
207 | $data[$name]['emailable'] = $user->canReceiveEmail(); |
||
208 | } |
||
209 | |||
210 | if ( isset( $this->prop['gender'] ) ) { |
||
211 | $gender = $user->getOption( 'gender' ); |
||
212 | if ( strval( $gender ) === '' ) { |
||
213 | $gender = 'unknown'; |
||
214 | } |
||
215 | $data[$name]['gender'] = $gender; |
||
216 | } |
||
217 | |||
218 | if ( isset( $this->prop['centralids'] ) ) { |
||
219 | $data[$name] += ApiQueryUserInfo::getCentralUserInfo( |
||
220 | $this->getConfig(), $user, $params['attachedwiki'] |
||
221 | ); |
||
222 | } |
||
223 | |||
224 | View Code Duplication | if ( !is_null( $params['token'] ) ) { |
|
225 | $tokenFunctions = $this->getTokenFunctions(); |
||
226 | foreach ( $params['token'] as $t ) { |
||
227 | $val = call_user_func( $tokenFunctions[$t], $user ); |
||
228 | if ( $val === false ) { |
||
229 | $this->setWarning( "Action '$t' is not allowed for the current user" ); |
||
230 | } else { |
||
231 | $data[$name][$t . 'token'] = $val; |
||
232 | } |
||
233 | } |
||
234 | } |
||
235 | } |
||
236 | } |
||
237 | |||
238 | $context = $this->getContext(); |
||
239 | // Second pass: add result data to $retval |
||
240 | foreach ( $goodNames as $u ) { |
||
241 | if ( !isset( $data[$u] ) ) { |
||
242 | $data[$u] = [ 'name' => $u ]; |
||
0 ignored issues
–
show
The variable
$data does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
243 | $urPage = new UserrightsPage; |
||
244 | $urPage->setContext( $context ); |
||
245 | $iwUser = $urPage->fetchUser( $u ); |
||
246 | |||
247 | if ( $iwUser instanceof UserRightsProxy ) { |
||
248 | $data[$u]['interwiki'] = true; |
||
249 | |||
250 | View Code Duplication | if ( !is_null( $params['token'] ) ) { |
|
251 | $tokenFunctions = $this->getTokenFunctions(); |
||
252 | |||
253 | foreach ( $params['token'] as $t ) { |
||
254 | $val = call_user_func( $tokenFunctions[$t], $iwUser ); |
||
255 | if ( $val === false ) { |
||
256 | $this->setWarning( "Action '$t' is not allowed for the current user" ); |
||
257 | } else { |
||
258 | $data[$u][$t . 'token'] = $val; |
||
259 | } |
||
260 | } |
||
261 | } |
||
262 | } else { |
||
263 | $data[$u]['missing'] = true; |
||
264 | if ( isset( $this->prop['cancreate'] ) ) { |
||
265 | $status = MediaWiki\Auth\AuthManager::singleton()->canCreateAccount( $u ); |
||
266 | $data[$u]['cancreate'] = $status->isGood(); |
||
267 | if ( !$status->isGood() ) { |
||
268 | $data[$u]['cancreateerror'] = $this->getErrorFormatter()->arrayFromStatus( $status ); |
||
269 | } |
||
270 | } |
||
271 | } |
||
272 | } else { |
||
273 | View Code Duplication | if ( isset( $this->prop['groups'] ) && isset( $data[$u]['groups'] ) ) { |
|
274 | ApiResult::setArrayType( $data[$u]['groups'], 'array' ); |
||
275 | ApiResult::setIndexedTagName( $data[$u]['groups'], 'g' ); |
||
276 | } |
||
277 | View Code Duplication | if ( isset( $this->prop['implicitgroups'] ) && isset( $data[$u]['implicitgroups'] ) ) { |
|
278 | ApiResult::setArrayType( $data[$u]['implicitgroups'], 'array' ); |
||
279 | ApiResult::setIndexedTagName( $data[$u]['implicitgroups'], 'g' ); |
||
280 | } |
||
281 | View Code Duplication | if ( isset( $this->prop['rights'] ) && isset( $data[$u]['rights'] ) ) { |
|
282 | ApiResult::setArrayType( $data[$u]['rights'], 'array' ); |
||
283 | ApiResult::setIndexedTagName( $data[$u]['rights'], 'r' ); |
||
284 | } |
||
285 | } |
||
286 | |||
287 | $fit = $result->addValue( [ 'query', $this->getModuleName() ], |
||
288 | null, $data[$u] ); |
||
289 | if ( !$fit ) { |
||
290 | $this->setContinueEnumParameter( 'users', |
||
291 | implode( '|', array_diff( $users, $done ) ) ); |
||
292 | break; |
||
293 | } |
||
294 | $done[] = $u; |
||
295 | } |
||
296 | $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'user' ); |
||
297 | } |
||
298 | |||
299 | public function getCacheMode( $params ) { |
||
300 | if ( isset( $params['token'] ) ) { |
||
301 | return 'private'; |
||
302 | } elseif ( array_diff( (array)$params['prop'], static::$publicProps ) ) { |
||
303 | return 'anon-public-user-private'; |
||
304 | } else { |
||
305 | return 'public'; |
||
306 | } |
||
307 | } |
||
308 | |||
309 | public function getAllowedParams() { |
||
310 | return [ |
||
311 | 'prop' => [ |
||
312 | ApiBase::PARAM_ISMULTI => true, |
||
313 | ApiBase::PARAM_TYPE => [ |
||
314 | 'blockinfo', |
||
315 | 'groups', |
||
316 | 'implicitgroups', |
||
317 | 'rights', |
||
318 | 'editcount', |
||
319 | 'registration', |
||
320 | 'emailable', |
||
321 | 'gender', |
||
322 | 'centralids', |
||
323 | 'cancreate', |
||
324 | // When adding a prop, consider whether it should be added |
||
325 | // to self::$publicProps |
||
326 | ], |
||
327 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [], |
||
328 | ], |
||
329 | 'attachedwiki' => null, |
||
330 | 'users' => [ |
||
331 | ApiBase::PARAM_ISMULTI => true |
||
332 | ], |
||
333 | 'token' => [ |
||
334 | ApiBase::PARAM_DEPRECATED => true, |
||
335 | ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ), |
||
336 | ApiBase::PARAM_ISMULTI => true |
||
337 | ], |
||
338 | ]; |
||
339 | } |
||
340 | |||
341 | protected function getExamplesMessages() { |
||
342 | return [ |
||
343 | 'action=query&list=users&ususers=Example&usprop=groups|editcount|gender' |
||
344 | => 'apihelp-query+users-example-simple', |
||
345 | ]; |
||
346 | } |
||
347 | |||
348 | public function getHelpUrls() { |
||
349 | return 'https://www.mediawiki.org/wiki/API:Users'; |
||
350 | } |
||
351 | } |
||
352 |
Only declaring a single property per statement allows you to later on add doc comments more easily.
It is also recommended by PSR2, so it is a common style that many people expect.