Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 72
0.00% covered (danger)
0.00%
0 / 28
CRAP
0.00% covered (danger)
0.00%
0 / 1
GlobalRenameRequest
0.00% covered (danger)
0.00%
0 / 72
0.00% covered (danger)
0.00%
0 / 28
1482
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWiki
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNewName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getReason
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRequested
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getStatus
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCompleted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDeleted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPerformer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getComments
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setId
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 setName
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setWiki
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setNewName
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 setReason
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setRequested
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setStatus
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setCompleted
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setDeleted
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setPerformer
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setComments
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 exists
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isPending
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 userIsGlobal
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 importRow
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 isNameAvailable
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
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
22namespace MediaWiki\Extension\CentralAuth\GlobalRename;
23
24use BadMethodCallException;
25use IDBAccessObject;
26use InvalidArgumentException;
27use MediaWiki\Extension\CentralAuth\CentralAuthServices;
28use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
29use MediaWiki\MediaWikiServices;
30use MediaWiki\Status\Status;
31use MediaWiki\User\UserNameUtils;
32use stdClass;
33
34/**
35 * Data access object for global rename requests.
36 *
37 * @author Bryan Davis <bd808@wikimedia.org>
38 * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
39 */
40class GlobalRenameRequest {
41
42    /** @var UserNameUtils */
43    private $userNameUtils;
44
45    public const PENDING = 'pending';
46    public const APPROVED = 'approved';
47    public const REJECTED = 'rejected';
48
49    /** @var int|null */
50    protected $id;
51    /** @var string|null */
52    protected $name;
53    /** @var string|null */
54    protected $wiki;
55    /** @var string|null */
56    protected $newName;
57    /** @var string|null */
58    protected $reason;
59    /** @var string|null */
60    protected $requested;
61    /** @var string|null */
62    protected $status;
63    /** @var string|null */
64    protected $completed;
65    /** @var int */
66    protected $deleted = 0;
67    /** @var int|null */
68    protected $performer;
69    /** @var string|null */
70    protected $comments;
71
72    /**
73     * @internal Use GlobalRenameRequestStore::newBlankRequest instead
74     * @param UserNameUtils $userNameUtils
75     */
76    public function __construct( UserNameUtils $userNameUtils ) {
77        $this->userNameUtils = $userNameUtils;
78    }
79
80    /**
81     * @return int
82     */
83    public function getId() {
84        return $this->id;
85    }
86
87    /**
88     * @return string Requesting user's name
89     */
90    public function getName() {
91        return $this->name;
92    }
93
94    /**
95     * @return string Requesting user's home wiki or null if CentralAuth user
96     */
97    public function getWiki() {
98        return $this->wiki;
99    }
100
101    /**
102     * @return string
103     */
104    public function getNewName() {
105        return $this->newName;
106    }
107
108    /**
109     * @return string User's reason for requesting rename
110     */
111    public function getReason() {
112        return $this->reason;
113    }
114
115    /**
116     * @return string MW timestamp that request was made
117     */
118    public function getRequested() {
119        return $this->requested;
120    }
121
122    /**
123     * @return string
124     */
125    public function getStatus() {
126        return $this->status;
127    }
128
129    /**
130     * @return string MW timestamp that request was processed
131     */
132    public function getCompleted() {
133        return $this->completed;
134    }
135
136    /**
137     * @return int Protection flags
138     */
139    public function getDeleted() {
140        return $this->deleted;
141    }
142
143    /**
144     * @return int CentralAuth user id of the user who processed the request
145     */
146    public function getPerformer() {
147        return $this->performer;
148    }
149
150    /**
151     * @return string
152     */
153    public function getComments() {
154        return $this->comments;
155    }
156
157    /**
158     * @param int $id
159     */
160    public function setId( int $id ) {
161        if ( $this->id !== null ) {
162            throw new BadMethodCallException( "Can't replace id when already set" );
163        }
164
165        $this->id = $id;
166    }
167
168    /**
169     * @param string $name
170     * @return GlobalRenameRequest self, for message chaining
171     */
172    public function setName( $name ) {
173        $this->name = $name;
174        return $this;
175    }
176
177    /**
178     * @param string $wiki
179     * @return GlobalRenameRequest self, for message chaining
180     */
181    public function setWiki( $wiki ) {
182        $this->wiki = $wiki;
183        return $this;
184    }
185
186    /**
187     * @param string $newName
188     * @return GlobalRenameRequest self, for message chaining
189     */
190    public function setNewName( $newName ) {
191        $canonicalName = $this->userNameUtils->getCanonical( $newName, UserNameUtils::RIGOR_CREATABLE );
192        if ( $canonicalName === false ) {
193            throw new InvalidArgumentException( "Invalid username '{$newName}'" );
194        }
195        $this->newName = $canonicalName;
196        return $this;
197    }
198
199    /**
200     * @param string $reason
201     * @return GlobalRenameRequest self, for message chaining
202     */
203    public function setReason( $reason ) {
204        $this->reason = $reason;
205        return $this;
206    }
207
208    /**
209     * @param string|null $requested MW timestamp, null for now
210     * @return GlobalRenameRequest self, for message chaining
211     */
212    public function setRequested( $requested = null ) {
213        $this->requested = $requested ?? wfTimestampNow();
214        return $this;
215    }
216
217    /**
218     * @param string $status
219     * @return GlobalRenameRequest self, for message chaining
220     */
221    public function setStatus( $status ) {
222        $this->status = $status;
223        return $this;
224    }
225
226    /**
227     * @param string|null $completed MW timestamp, null for now
228     * @return GlobalRenameRequest self, for message chaining
229     */
230    public function setCompleted( $completed = null ) {
231        $this->completed = $completed ?? wfTimestampNow();
232        return $this;
233    }
234
235    /**
236     * @param int $deleted Bitmask
237     * @return GlobalRenameRequest self, for message chaining
238     */
239    public function setDeleted( $deleted ) {
240        $this->deleted = $deleted;
241        return $this;
242    }
243
244    /**
245     * @param int $performer
246     * @return GlobalRenameRequest self, for message chaining
247     */
248    public function setPerformer( $performer ) {
249        $this->performer = $performer;
250        return $this;
251    }
252
253    /**
254     * @param string $comments
255     * @return GlobalRenameRequest self, for message chaining
256     */
257    public function setComments( $comments ) {
258        $this->comments = $comments;
259        return $this;
260    }
261
262    /**
263     * @return bool
264     */
265    public function exists() {
266        return $this->id !== null;
267    }
268
269    /**
270     * @return bool
271     */
272    public function isPending() {
273        return $this->status === self::PENDING;
274    }
275
276    /**
277     * @return bool
278     */
279    public function userIsGlobal() {
280        return $this->wiki === null;
281    }
282
283    /**
284     * @internal
285     * @param stdClass $row Database row
286     */
287    public function importRow( stdClass $row ) {
288        $this->id = $row->id;
289        $this->name = $row->name;
290        $this->wiki = $row->wiki;
291        $this->newName = $row->newname;
292        $this->reason = $row->reason;
293        $this->requested = wfTimestampOrNull( TS_MW, $row->requested );
294        $this->status = $row->status;
295        $this->completed = wfTimestampOrNull( TS_MW, $row->completed );
296        $this->deleted = $row->deleted;
297        $this->performer = $row->performer;
298        $this->comments = $row->comments;
299    }
300
301    /**
302     * Check to see if a given username is available for use via CentralAuth.
303     *
304     * Note that this is not a definitive check. It does not include checking
305     * for AntiSpoof, TitleBlacklist or other AbortNewAccount hook blocks.
306     * Unfortunately the only canonical way to validate that an account is
307     * available is to make the account and check that it wasn't blocked by
308     * something.
309     *
310     * @param string $name
311     * @param int $flags one of IDBAccessObject::READ_* flags
312     * @return Status Canonicalized name
313     */
314    public static function isNameAvailable( string $name, int $flags = IDBAccessObject::READ_LATEST ) {
315        $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils();
316        $safe = $userNameUtils->getCanonical( $name, UserNameUtils::RIGOR_CREATABLE );
317        $status = Status::newGood( $safe );
318
319        if ( $safe === false || $safe === '' ) {
320            $status->fatal( 'globalrenamerequest-newname-err-invalid' );
321            return $status;
322        }
323
324        if ( CentralAuthServices::getGlobalRenameRequestStore()->nameHasPendingRequest( $safe, $flags ) ) {
325            $status->fatal( 'globalrenamerequest-newname-err-taken' );
326            return $status;
327        }
328
329        // New user creation checks against local wiki only using an API
330        // request, but we need to check against the central user table instead
331
332        if ( $flags & IDBAccessObject::READ_LATEST ) {
333            $centralUser = CentralAuthUser::getPrimaryInstanceByName( $safe );
334        } else {
335            $centralUser = CentralAuthUser::getInstanceByName( $safe );
336        }
337
338        if ( $centralUser->exists() || $centralUser->listUnattached() ) {
339            $status->fatal( 'globalrenamerequest-newname-err-taken' );
340            return $status;
341        }
342
343        // Check to see if there is an active rename to the desired name.
344        $progress = $centralUser->renameInProgress();
345        if ( $progress && $safe == $progress[1] ) {
346            $status->fatal( 'globalrenamerequest-newname-err-taken' );
347            return $status;
348        }
349
350        return $status;
351    }
352
353}