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