Follow up to r47976 (bug 17722). For real this time: fix the regression where users...
[lhc/web/wiklou.git] / includes / specials / SpecialResetpass.php
1 <?php
2 /**
3 * @file
4 * @ingroup SpecialPage
5 */
6
7 /**
8 * Let users recover their password.
9 * @ingroup SpecialPage
10 */
11 class SpecialResetpass extends SpecialPage {
12
13 private $mSelfChange = true; // Usually, but sometimes not :)
14 private $mUser = null; // The user requesting the reset
15
16 public function __construct() {
17 parent::__construct( 'Resetpass' );
18 }
19
20 /**
21 * Sometimes the user requesting the password change is not $wgUser
22 * See bug 17722
23 * @param User $usr
24 */
25 public function setUser( $usr ) {
26 $this->mUser = $usr;
27 }
28
29 /**
30 * Main execution point
31 */
32 function execute( $par ) {
33 global $wgUser, $wgAuth, $wgOut, $wgRequest;
34
35 $this->mUserName = $wgRequest->getVal( 'wpName', $par );
36 $this->mOldpass = $wgRequest->getVal( 'wpPassword' );
37 $this->mNewpass = $wgRequest->getVal( 'wpNewPassword' );
38 $this->mRetype = $wgRequest->getVal( 'wpRetype' );
39 $this->mComment = $wgRequest->getVal( 'wpComment' );
40
41 if ( is_null( $this->mUser ) ) {
42 $this->mUser = $wgUser;
43 }
44
45 $this->setHeaders();
46 $this->outputHeader();
47
48 if( !$wgAuth->allowPasswordChange() ) {
49 $this->error( wfMsg( 'resetpass_forbidden' ) );
50 return;
51 }
52
53 // Default to our own username when not given one
54 if ( !$this->mUserName ) {
55 $this->mUserName = $this->mUser->getName();
56 }
57
58 // Are we changing our own?
59 if ( $this->mUser->getName() != $this->mUserName ) {
60 $this->mSelfChange = false; // We're changing someone else
61 }
62
63 if( !$wgRequest->wasPosted() && !$this->mUser->isLoggedIn() ) {
64 $this->error( wfMsg( 'resetpass-no-info' ) );
65 return;
66 }
67
68 if ( !$this->mSelfChange && !$this->mUser->isAllowed( 'reset-passwords' ) ) {
69 $this->error( wfMsg( 'resetpass-no-others' ) );
70 return;
71 }
72
73 if( $wgRequest->wasPosted() && $this->mUser->matchEditToken( $wgRequest->getVal('token') ) ) {
74 try {
75 $this->attemptReset( $this->mNewpass, $this->mRetype );
76 $wgOut->addWikiMsg( 'resetpass_success' );
77 // Only attempt this login session if we're changing our own password
78 if( $this->mSelfChange && !$wgUser->isLoggedIn() ) {
79 $data = array(
80 'action' => 'submitlogin',
81 'wpName' => $this->mUserName,
82 'wpPassword' => $this->mNewpass,
83 'returnto' => $wgRequest->getVal( 'returnto' ),
84 );
85 if( $wgRequest->getCheck( 'wpRemember' ) ) {
86 $data['wpRemember'] = 1;
87 }
88 $login = new LoginForm( new FauxRequest( $data, true ) );
89 $login->execute();
90 }
91 $titleObj = Title::newFromText( $wgRequest->getVal( 'returnto' ) );
92 if ( !$titleObj instanceof Title ) {
93 $titleObj = Title::newMainPage();
94 }
95 $wgOut->redirect( $titleObj->getFullURL() );
96 } catch( PasswordError $e ) {
97 $this->error( $e->getMessage() );
98 }
99 }
100 $this->showForm();
101 }
102
103 function error( $msg ) {
104 global $wgOut;
105 $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $msg ) );
106 }
107
108 function showForm() {
109 global $wgOut, $wgUser, $wgRequest;
110
111 $wgOut->disallowUserJs();
112
113 if ( $this->mUser->isAllowed( 'reset-passwords') ) {
114 $wgOut->addScriptFile( 'changepassword.js' );
115 }
116
117 $self = SpecialPage::getTitleFor( 'Resetpass' );
118
119 $rememberMe = '';
120 if ( !$this->mUser->isLoggedIn() ) {
121 $rememberMe = '<tr>' .
122 '<td></td>' .
123 '<td class="mw-input">' .
124 Xml::checkLabel( wfMsg( 'remembermypassword' ),
125 'wpRemember', 'wpRemember',
126 $wgRequest->getCheck( 'wpRemember' ) ) .
127 '</td>' .
128 '</tr>';
129 $submitMsg = 'resetpass_submit';
130 $oldpassMsg = 'resetpass-temp-password';
131 } else {
132 $oldpassMsg = 'oldpassword';
133 $submitMsg = 'resetpass-submit-loggedin';
134 }
135 $s = Xml::fieldset( wfMsg( 'resetpass_header' ) ) .
136 Xml::openElement( 'form',
137 array(
138 'method' => 'post',
139 'action' => $self->getLocalUrl(),
140 'id' => 'mw-resetpass-form' ) ) .
141 Xml::hidden( 'token', $this->mUser->editToken() ) .
142 Xml::hidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) .
143 wfMsgExt( 'resetpass_text', array( 'parse' ) ) .
144 Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) );
145 $formElements = array(
146 array( 'wpName', 'username', 'text', $this->mUserName, $this->mUser->isAllowed( 'reset-passwords' ) ),
147 array( 'wpPassword', $oldpassMsg, 'password', $this->mOldpass, $this->mSelfChange ),
148 array( 'wpNewPassword', 'newpassword', 'password', '', true ),
149 array( 'wpRetype', 'retypenew', 'password', '', true ) );
150 if ( $this->mUser->isAllowed( 'reset-passwords' ) && $this->mSelfChange )
151 $formElements[] = array( 'wpComment', 'resetpass-comment', 'text', $this->mComment, true );
152 $s .= $this->pretty( $formElements ) .
153 $rememberMe .
154 '<tr>' .
155 '<td></td>' .
156 '<td class="mw-input">' .
157 Xml::submitButton( wfMsg( $submitMsg ) ) .
158 '</td>' .
159 '</tr>' .
160 Xml::closeElement( 'table' ) .
161 Xml::closeElement( 'form' ) .
162 Xml::closeElement( 'fieldset' );
163 $wgOut->addHtml( $s );
164 }
165
166 function pretty( $fields ) {
167 $out = '';
168 foreach( $fields as $list ) {
169 list( $name, $label, $type, $value, $enabled ) = $list;
170 $params = array( 'id' => $name, 'type' => $type );
171 if ( !$enabled )
172 $params['disabled'] = 'disabled';
173 $field = Xml::input( $name, 20, $value, $params );
174 $out .= '<tr>';
175 $out .= '<td class="mw-label">';
176 $out .= Xml::label( wfMsg( $label ), $name );
177 $out .= '</td>';
178 $out .= '<td class="mw-input">';
179 $out .= $field;
180 $out .= '</td>';
181 $out .= '</tr>';
182 }
183 return $out;
184 }
185
186 /**
187 * @throws PasswordError when cannot set the new password because requirements not met.
188 */
189 protected function attemptReset( $newpass, $retype ) {
190 $user = User::newFromName( $this->mUserName );
191 if( !$user || $user->isAnon() ) {
192 throw new PasswordError( 'no such user' );
193 }
194
195 if( $newpass !== $retype ) {
196 wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'badretype' ) );
197 throw new PasswordError( wfMsg( 'badretype' ) );
198 }
199
200 if ( $this->mSelfChange ) {
201 if( !$user->checkTemporaryPassword($this->mOldpass) && !$user->checkPassword($this->mOldpass) ) {
202 wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) );
203 throw new PasswordError( wfMsg( 'resetpass-wrong-oldpass' ) );
204 }
205 }
206
207 try {
208 $user->setPassword( $this->mNewpass );
209 wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) );
210 $this->mNewpass = $this->mOldpass = $this->mRetypePass = '';
211 } catch( PasswordError $e ) {
212 wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) );
213 throw new PasswordError( $e->getMessage() );
214 return;
215 }
216
217 if ( !$this->mSelfChange ) {
218 $log = new LogPage( 'password' );
219 $log->addEntry( 'reset', $user->getUserPage(), $this->mComment );
220 } else {
221 // Only set cookies if it was a self-change
222 $user->setCookies();
223 }
224
225 $user->saveSettings();
226 }
227 }