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