Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 90 |
|
0.00% |
0 / 31 |
CRAP | |
0.00% |
0 / 1 |
GlobalRenameRequest | |
0.00% |
0 / 90 |
|
0.00% |
0 / 31 |
1722 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getWiki | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNewName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getReason | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRequested | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCompleted | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDeleted | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPerformer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getComments | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setId | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
setName | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setWiki | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setNewName | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
setReason | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setRequested | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setStatus | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setCompleted | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setDeleted | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setPerformer | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setComments | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setType | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
exists | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isPending | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
userIsGlobal | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
importRow | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
2 | |||
toArray | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
isNameAvailable | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | /** |
3 | * @section LICENSE |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License along |
15 | * with this program; if not, write to the Free Software Foundation, Inc., |
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | * http://www.gnu.org/copyleft/gpl.html |
18 | * |
19 | * @file |
20 | */ |
21 | |
22 | namespace MediaWiki\Extension\CentralAuth\GlobalRename; |
23 | |
24 | use BadMethodCallException; |
25 | use InvalidArgumentException; |
26 | use MediaWiki\Extension\CentralAuth\CentralAuthServices; |
27 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
28 | use MediaWiki\MediaWikiServices; |
29 | use MediaWiki\Status\Status; |
30 | use MediaWiki\User\UserNameUtils; |
31 | use stdClass; |
32 | use Wikimedia\Rdbms\DBAccessObjectUtils; |
33 | use Wikimedia\Rdbms\IDBAccessObject; |
34 | |
35 | /** |
36 | * Data access object for global rename requests. |
37 | * |
38 | * @author Bryan Davis <bd808@wikimedia.org> |
39 | * @copyright © 2014 Bryan Davis and Wikimedia Foundation. |
40 | */ |
41 | class GlobalRenameRequest { |
42 | |
43 | private UserNameUtils $userNameUtils; |
44 | |
45 | public const PENDING = 'pending'; |
46 | public const APPROVED = 'approved'; |
47 | public const REJECTED = 'rejected'; |
48 | |
49 | public const RENAME = 0; |
50 | public const VANISH = 1; |
51 | |
52 | /** @var int|null */ |
53 | protected $id; |
54 | /** @var string|null */ |
55 | protected $name; |
56 | /** @var string|null */ |
57 | protected $wiki; |
58 | /** @var string|null */ |
59 | protected $newName; |
60 | /** @var string|null */ |
61 | protected $reason; |
62 | /** @var string|null */ |
63 | protected $requested; |
64 | /** @var string|null */ |
65 | protected $status; |
66 | /** @var string|null */ |
67 | protected $completed; |
68 | /** @var int */ |
69 | protected $deleted = 0; |
70 | /** @var int|null */ |
71 | protected $performer; |
72 | /** @var string|null */ |
73 | protected $comments; |
74 | /** @var int|null */ |
75 | protected $type; |
76 | |
77 | /** |
78 | * @internal Use GlobalRenameRequestStore::newBlankRequest instead |
79 | * @param UserNameUtils $userNameUtils |
80 | */ |
81 | public function __construct( UserNameUtils $userNameUtils ) { |
82 | $this->userNameUtils = $userNameUtils; |
83 | } |
84 | |
85 | /** |
86 | * @return int |
87 | */ |
88 | public function getId() { |
89 | return $this->id; |
90 | } |
91 | |
92 | /** |
93 | * @return string Requesting user's name |
94 | */ |
95 | public function getName() { |
96 | return $this->name; |
97 | } |
98 | |
99 | /** |
100 | * @return string Requesting user's home wiki or null if CentralAuth user |
101 | */ |
102 | public function getWiki() { |
103 | return $this->wiki; |
104 | } |
105 | |
106 | /** |
107 | * @return string |
108 | */ |
109 | public function getNewName() { |
110 | return $this->newName; |
111 | } |
112 | |
113 | /** |
114 | * @return string User's reason for requesting rename |
115 | */ |
116 | public function getReason() { |
117 | return $this->reason; |
118 | } |
119 | |
120 | /** |
121 | * @return string MW timestamp that request was made |
122 | */ |
123 | public function getRequested() { |
124 | return $this->requested; |
125 | } |
126 | |
127 | /** |
128 | * @return string |
129 | */ |
130 | public function getStatus() { |
131 | return $this->status; |
132 | } |
133 | |
134 | /** |
135 | * @return string MW timestamp that request was processed |
136 | */ |
137 | public function getCompleted() { |
138 | return $this->completed; |
139 | } |
140 | |
141 | /** |
142 | * @return int Protection flags |
143 | */ |
144 | public function getDeleted() { |
145 | return $this->deleted; |
146 | } |
147 | |
148 | /** |
149 | * @return int CentralAuth user id of the user who processed the request |
150 | */ |
151 | public function getPerformer() { |
152 | return $this->performer; |
153 | } |
154 | |
155 | /** |
156 | * @return string |
157 | */ |
158 | public function getComments() { |
159 | return $this->comments; |
160 | } |
161 | |
162 | /** |
163 | * @return int |
164 | */ |
165 | public function getType() { |
166 | return $this->type; |
167 | } |
168 | |
169 | /** |
170 | * @param int $id |
171 | */ |
172 | public function setId( int $id ) { |
173 | if ( $this->id !== null ) { |
174 | throw new BadMethodCallException( "Can't replace id when already set" ); |
175 | } |
176 | |
177 | $this->id = $id; |
178 | } |
179 | |
180 | /** |
181 | * @param string $name |
182 | * @return GlobalRenameRequest self, for message chaining |
183 | */ |
184 | public function setName( $name ) { |
185 | $this->name = $name; |
186 | return $this; |
187 | } |
188 | |
189 | /** |
190 | * @param string $wiki |
191 | * @return GlobalRenameRequest self, for message chaining |
192 | */ |
193 | public function setWiki( $wiki ) { |
194 | $this->wiki = $wiki; |
195 | return $this; |
196 | } |
197 | |
198 | /** |
199 | * @param string $newName |
200 | * @return GlobalRenameRequest self, for message chaining |
201 | */ |
202 | public function setNewName( $newName ) { |
203 | $canonicalName = $this->userNameUtils->getCanonical( $newName, UserNameUtils::RIGOR_CREATABLE ); |
204 | if ( $canonicalName === false ) { |
205 | throw new InvalidArgumentException( "Invalid username '{$newName}'" ); |
206 | } |
207 | $this->newName = $canonicalName; |
208 | return $this; |
209 | } |
210 | |
211 | /** |
212 | * @param string $reason |
213 | * @return GlobalRenameRequest self, for message chaining |
214 | */ |
215 | public function setReason( $reason ) { |
216 | $this->reason = $reason; |
217 | return $this; |
218 | } |
219 | |
220 | /** |
221 | * @param string|null $requested MW timestamp, null for now |
222 | * @return GlobalRenameRequest self, for message chaining |
223 | */ |
224 | public function setRequested( $requested = null ) { |
225 | $this->requested = $requested ?? wfTimestampNow(); |
226 | return $this; |
227 | } |
228 | |
229 | /** |
230 | * @param string $status |
231 | * @return GlobalRenameRequest self, for message chaining |
232 | */ |
233 | public function setStatus( $status ) { |
234 | $this->status = $status; |
235 | return $this; |
236 | } |
237 | |
238 | /** |
239 | * @param string|null $completed MW timestamp, null for now |
240 | * @return GlobalRenameRequest self, for message chaining |
241 | */ |
242 | public function setCompleted( $completed = null ) { |
243 | $this->completed = $completed ?? wfTimestampNow(); |
244 | return $this; |
245 | } |
246 | |
247 | /** |
248 | * @param int $deleted Bitmask |
249 | * @return GlobalRenameRequest self, for message chaining |
250 | */ |
251 | public function setDeleted( $deleted ) { |
252 | $this->deleted = $deleted; |
253 | return $this; |
254 | } |
255 | |
256 | /** |
257 | * @param int $performer |
258 | * @return GlobalRenameRequest self, for message chaining |
259 | */ |
260 | public function setPerformer( $performer ) { |
261 | $this->performer = $performer; |
262 | return $this; |
263 | } |
264 | |
265 | /** |
266 | * @param string $comments |
267 | * @return GlobalRenameRequest self, for message chaining |
268 | */ |
269 | public function setComments( $comments ) { |
270 | $this->comments = $comments; |
271 | return $this; |
272 | } |
273 | |
274 | /** |
275 | * @param int $type |
276 | * @return GlobalRenameRequest self, for message chaining |
277 | */ |
278 | public function setType( $type ) { |
279 | $this->type = $type; |
280 | return $this; |
281 | } |
282 | |
283 | /** |
284 | * @return bool |
285 | */ |
286 | public function exists() { |
287 | return $this->id !== null; |
288 | } |
289 | |
290 | /** |
291 | * @return bool |
292 | */ |
293 | public function isPending() { |
294 | return $this->status === self::PENDING; |
295 | } |
296 | |
297 | /** |
298 | * @return bool |
299 | */ |
300 | public function userIsGlobal() { |
301 | return $this->wiki === null; |
302 | } |
303 | |
304 | /** |
305 | * @internal |
306 | * @param stdClass $row Database row |
307 | */ |
308 | public function importRow( stdClass $row ) { |
309 | $this->id = $row->id; |
310 | $this->name = $row->name; |
311 | $this->wiki = $row->wiki; |
312 | $this->newName = $row->newname; |
313 | $this->reason = $row->reason; |
314 | $this->requested = wfTimestampOrNull( TS_MW, $row->requested ); |
315 | $this->status = $row->status; |
316 | $this->completed = wfTimestampOrNull( TS_MW, $row->completed ); |
317 | $this->deleted = $row->deleted; |
318 | $this->performer = $row->performer; |
319 | $this->comments = $row->comments; |
320 | $this->type = (int)$row->type; |
321 | } |
322 | |
323 | /** |
324 | * @internal |
325 | * @return array array representation of the rename request |
326 | */ |
327 | public function toArray(): array { |
328 | return [ |
329 | 'id' => $this->id, |
330 | 'name' => $this->name, |
331 | 'wiki' => $this->wiki, |
332 | 'newname' => $this->newName, |
333 | 'reason' => $this->reason, |
334 | 'requested' => $this->requested, |
335 | 'status' => $this->status, |
336 | 'completed' => $this->completed, |
337 | 'deleted' => $this->deleted, |
338 | 'performer' => $this->performer, |
339 | 'comments' => $this->comments, |
340 | 'type' => $this->type, |
341 | ]; |
342 | } |
343 | |
344 | /** |
345 | * Check to see if a given username is available for use via CentralAuth. |
346 | * |
347 | * Note that this is not a definitive check. It does not include checking |
348 | * for AntiSpoof, TitleBlacklist or other AbortNewAccount hook blocks. |
349 | * Unfortunately the only canonical way to validate that an account is |
350 | * available is to make the account and check that it wasn't blocked by |
351 | * something. |
352 | * |
353 | * @param string $name |
354 | * @param int $flags one of IDBAccessObject::READ_* flags |
355 | * @return Status Canonicalized name |
356 | */ |
357 | public static function isNameAvailable( string $name, int $flags = IDBAccessObject::READ_LATEST ) { |
358 | $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils(); |
359 | $safe = $userNameUtils->getCanonical( $name, UserNameUtils::RIGOR_CREATABLE ); |
360 | $status = Status::newGood( $safe ); |
361 | |
362 | if ( $safe === false || $safe === '' ) { |
363 | $status->fatal( 'globalrenamerequest-newname-err-invalid' ); |
364 | return $status; |
365 | } |
366 | |
367 | if ( CentralAuthServices::getGlobalRenameRequestStore()->nameHasPendingRequest( $safe, $flags ) ) { |
368 | $status->fatal( 'globalrenamerequest-newname-err-taken' ); |
369 | return $status; |
370 | } |
371 | |
372 | // New user creation checks against local wiki only using an API |
373 | // request, but we need to check against the central user table instead |
374 | |
375 | if ( DBAccessObjectUtils::hasFlags( $flags, IDBAccessObject::READ_LATEST ) ) { |
376 | $centralUser = CentralAuthUser::getPrimaryInstanceByName( $safe ); |
377 | } else { |
378 | $centralUser = CentralAuthUser::getInstanceByName( $safe ); |
379 | } |
380 | |
381 | if ( $centralUser->exists() || $centralUser->listUnattached() ) { |
382 | $status->fatal( 'globalrenamerequest-newname-err-taken' ); |
383 | return $status; |
384 | } |
385 | |
386 | // Check to see if there is an active rename to the desired name. |
387 | $progress = $centralUser->renameInProgress(); |
388 | if ( $progress && $safe == $progress[1] ) { |
389 | $status->fatal( 'globalrenamerequest-newname-err-taken' ); |
390 | return $status; |
391 | } |
392 | |
393 | return $status; |
394 | } |
395 | |
396 | } |