Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.36% covered (warning)
86.36%
57 / 66
45.45% covered (danger)
45.45%
5 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialPasswordReset
87.69% covered (warning)
87.69%
57 / 65
45.45% covered (danger)
45.45%
5 / 11
28.36
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 userCanExecute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkExecutePermissions
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 execute
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getFormFields
94.12% covered (success)
94.12%
16 / 17
0.00% covered (danger)
0.00%
0 / 1
6.01
 getDisplayFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 alterForm
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
 onSubmit
95.24% covered (success)
95.24%
20 / 21
0.00% covered (danger)
0.00%
0 / 1
5
 isListed
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
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 * @file
19 */
20
21namespace MediaWiki\Specials;
22
23use MediaWiki\Exception\ErrorPageError;
24use MediaWiki\Exception\ThrottledError;
25use MediaWiki\HTMLForm\HTMLForm;
26use MediaWiki\MainConfigNames;
27use MediaWiki\SpecialPage\FormSpecialPage;
28use MediaWiki\Status\Status;
29use MediaWiki\User\PasswordReset;
30use MediaWiki\User\User;
31
32/**
33 * Special page for requesting a password reset email.
34 *
35 * Requires the TemporaryPasswordPrimaryAuthenticationProvider and the
36 * EmailNotificationSecondaryAuthenticationProvider (or something providing equivalent
37 * functionality) to be enabled.
38 *
39 * @ingroup SpecialPage
40 */
41class SpecialPasswordReset extends FormSpecialPage {
42    private PasswordReset $passwordReset;
43
44    public function __construct( PasswordReset $passwordReset ) {
45        parent::__construct( 'PasswordReset', 'editmyprivateinfo' );
46
47        $this->passwordReset = $passwordReset;
48    }
49
50    public function doesWrites() {
51        return true;
52    }
53
54    /** @inheritDoc */
55    public function userCanExecute( User $user ) {
56        return $this->passwordReset->isAllowed( $user )->isGood();
57    }
58
59    public function checkExecutePermissions( User $user ) {
60        $status = Status::wrap( $this->passwordReset->isAllowed( $user ) );
61        if ( !$status->isGood() ) {
62            throw new ErrorPageError( 'internalerror', $status->getMessage() );
63        }
64
65        parent::checkExecutePermissions( $user );
66    }
67
68    /**
69     * @param string|null $par
70     */
71    public function execute( $par ) {
72        $out = $this->getOutput();
73        $out->disallowUserJs();
74        parent::execute( $par );
75    }
76
77    /** @inheritDoc */
78    protected function getFormFields() {
79        $resetRoutes = $this->getConfig()->get( MainConfigNames::PasswordResetRoutes );
80        $a = [];
81        if ( isset( $resetRoutes['username'] ) && $resetRoutes['username'] ) {
82            $a['Username'] = [
83                'type' => 'user',
84                'default' => $this->getRequest()->getSession()->suggestLoginUsername(),
85                'label-message' => 'passwordreset-username',
86                'excludetemp' => true,
87            ];
88
89            if ( $this->getUser()->isRegistered() ) {
90                $a['Username']['default'] = $this->getUser()->getName();
91            }
92        }
93
94        if ( isset( $resetRoutes['email'] ) && $resetRoutes['email'] ) {
95            $a['Email'] = [
96                'type' => 'email',
97                'label-message' => 'passwordreset-email',
98            ];
99        }
100
101        return $a;
102    }
103
104    /** @inheritDoc */
105    protected function getDisplayFormat() {
106        return 'ooui';
107    }
108
109    public function alterForm( HTMLForm $form ) {
110        $resetRoutes = $this->getConfig()->get( MainConfigNames::PasswordResetRoutes );
111
112        $form->setSubmitDestructive();
113
114        $form->addHiddenFields( $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
115
116        $i = 0;
117        if ( isset( $resetRoutes['username'] ) && $resetRoutes['username'] ) {
118            $i++;
119        }
120        if ( isset( $resetRoutes['email'] ) && $resetRoutes['email'] ) {
121            $i++;
122        }
123
124        $message = ( $i > 1 ) ? 'passwordreset-text-many' : 'passwordreset-text-one';
125
126        $form->setHeaderHtml( $this->msg( $message, $i )->parseAsBlock() );
127        $form->setSubmitTextMsg( 'mailmypassword' );
128    }
129
130    /**
131     * Process the form.
132     * At this point, we know that the user passes all the criteria in
133     * userCanExecute(), and if the data array contains 'Username', etc., then Username
134     * resets are allowed.
135     * @param array $data
136     * @return Status
137     */
138    public function onSubmit( array $data ) {
139        $username = $data['Username'] ?? null;
140        $email = $data['Email'] ?? null;
141
142        $result = Status::wrap(
143            $this->passwordReset->execute( $this->getUser(), $username, $email ) );
144
145        if ( $result->hasMessage( 'actionthrottledtext' ) ) {
146            throw new ThrottledError;
147        }
148
149        // Show a message on the successful processing of the form.
150        // This doesn't necessarily mean a reset email was sent.
151        if ( $result->isGood() ) {
152            $output = $this->getOutput();
153
154            // Information messages.
155            $output->addWikiMsg( 'passwordreset-success' );
156            $output->addWikiMsg( 'passwordreset-success-details-generic',
157                $this->getConfig()->get( MainConfigNames::PasswordReminderResendTime ) );
158
159            // Confirmation of what the user has just submitted.
160            $info = "\n";
161            if ( $username ) {
162                $info .= "* " . $this->msg( 'passwordreset-username' ) . ' '
163                    . wfEscapeWikiText( $username ) . "\n";
164            }
165            if ( $email ) {
166                $info .= "* " . $this->msg( 'passwordreset-email' ) . ' '
167                    . wfEscapeWikiText( $email ) . "\n";
168            }
169            $output->addWikiMsg( 'passwordreset-success-info', $info );
170
171            // Add a return to link to the main page.
172            $output->returnToMain();
173        }
174
175        return $result;
176    }
177
178    /**
179     * Hide the password reset page if resets are disabled.
180     * @return bool
181     */
182    public function isListed() {
183        if ( !$this->passwordReset->isEnabled()->isGood() ) {
184            return false;
185        }
186
187        return parent::isListed();
188    }
189
190    /** @inheritDoc */
191    protected function getGroupName() {
192        return 'login';
193    }
194}
195
196/**
197 * Retain the old class name for backwards compatibility.
198 * @deprecated since 1.41
199 */
200class_alias( SpecialPasswordReset::class, 'SpecialPasswordReset' );