Index: branches/happy-melon/phase3/skins/common/shared.css |
— | — | @@ -804,3 +804,28 @@ |
805 | 805 | } |
806 | 806 | |
807 | 807 | #wpLoginAttempt, #wpCreateaccount { margin-right:0; } |
| 808 | +.mw-small-spinner { |
| 809 | + padding: 10px !important; |
| 810 | + margin-right: 0.6em; |
| 811 | + background-image: url(images/spinner.gif); |
| 812 | + background-position: center center; |
| 813 | + background-repeat: no-repeat; |
| 814 | +} |
| 815 | + |
| 816 | +/* Sort arrows added by SortableTables */ |
| 817 | +a.sortheader { |
| 818 | + margin: 0 0.3em; |
| 819 | +} |
| 820 | + |
| 821 | +.grid-table { |
| 822 | + display: table; |
| 823 | +} |
| 824 | + |
| 825 | +.grid-row { |
| 826 | + display: table-row; |
| 827 | +} |
| 828 | + |
| 829 | +.grid-cell { |
| 830 | + display: table-cell; |
| 831 | + padding: 0.33ex; |
| 832 | +} |
Index: branches/happy-melon/phase3/includes/User.php |
— | — | @@ -5,12 +5,6 @@ |
6 | 6 | */ |
7 | 7 | |
8 | 8 | /** |
9 | | - * \int Number of characters in user_token field. |
10 | | - * @ingroup Constants |
11 | | - */ |
12 | | -define( 'USER_TOKEN_LENGTH', 32 ); |
13 | | - |
14 | | -/** |
15 | 9 | * \int Serialized record version. |
16 | 10 | * @ingroup Constants |
17 | 11 | */ |
— | — | @@ -2968,7 +2962,7 @@ |
2969 | 2963 | $now = time(); |
2970 | 2964 | $expires = $now + 7 * 24 * 60 * 60; |
2971 | 2965 | $expiration = wfTimestamp( TS_MW, $expires ); |
2972 | | - $token = $this->generateToken( $this->mId . $this->mEmail . $expires ); |
| 2966 | + $token = wfGenerateToken( $this->mId . $this->mEmail . $expires ); |
2973 | 2967 | $hash = md5( $token ); |
2974 | 2968 | $this->load(); |
2975 | 2969 | $this->mEmailToken = $hash; |
Index: branches/happy-melon/phase3/includes/Token.php |
— | — | @@ -0,0 +1,137 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * CSRF attacks (where a malicious website uses frames, <img> tags, or |
| 5 | + * similar, to prompt a wiki user to open a wiki page or submit a form, |
| 6 | + * without being aware of doing so) are most easily countered by using |
| 7 | + * tokens. For normal browsing, loading the form for a protected action |
| 8 | + * sets two copies of a random string: one in the $_SESSION, and one as |
| 9 | + * a hidden field in the form. When the form is submitted, it checks |
| 10 | + * that a) the set of cookies submitted with the form *has* a copy of |
| 11 | + * the session cookie, and b) that it matches. Since malicious websites |
| 12 | + * don't have control over the session cookies, they can't craft a form |
| 13 | + * that can be instantly submitted which will have the appropriate tokens. |
| 14 | + * |
| 15 | + * Note that these tokens are distinct from those in User::setToken(), which |
| 16 | + * are used for persistent session authentication and are retained for as |
| 17 | + * long as the user is logged in to the wiki. These tokens are to protect |
| 18 | + * one individual action, and should ideally be cleared once the action is over. |
| 19 | + */ |
| 20 | + |
| 21 | +class Token { |
| 22 | + # Some punctuation to prevent editing from broken |
| 23 | + # text-mangling proxies. |
| 24 | + const TOKEN_SUFFIX = '+\\'; |
| 25 | + |
| 26 | + /** |
| 27 | + * Ensure that a token is set in cookies, by setting a new one |
| 28 | + * if necessary. |
| 29 | + * |
| 30 | + * @param $action String the action that's being protected by the token |
| 31 | + * @param $salt Mixed String, Array Optional function-specific |
| 32 | + * data for hashing |
| 33 | + * @return \string The token that was set |
| 34 | + */ |
| 35 | + public static function set( $action='edit', $salt='' ) { |
| 36 | + # The 'generic' token is EditToken, which we don't store for anons |
| 37 | + # so they can still do things when they have cookies disabled. |
| 38 | + # So either use this for actions which annons can't access, or |
| 39 | + # where you don't mind an attacker being able to trigger the action |
| 40 | + # anonymously from the user's IP. However, the token is still |
| 41 | + # useful because it fails with some broken proxies. |
| 42 | + global $wgUser; |
| 43 | + if ( $action == 'edit' && $wgUser->isAnon() ) { |
| 44 | + return self::TOKEN_SUFFIX; |
| 45 | + } |
| 46 | + |
| 47 | + if( !self::has( $action ) ){ |
| 48 | + $token = self::generate(); |
| 49 | + if( session_id() == '' ) { |
| 50 | + wfSetupSession(); |
| 51 | + } |
| 52 | + self::store( $token, $action ); |
| 53 | + } else { |
| 54 | + $token = self::get( $action ); |
| 55 | + } |
| 56 | + |
| 57 | + if( is_array( $salt ) ) { |
| 58 | + $salt = implode( '|', $salt ); |
| 59 | + } |
| 60 | + |
| 61 | + return md5( $token . $salt ) . self::TOKEN_SUFFIX; |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * Check whether the copy of the token submitted with a form |
| 66 | + * matches the version stored in session |
| 67 | + * @param $val String version submitted with the form. If null, |
| 68 | + * tries to get it from $wgRequest |
| 69 | + * @param $action String |
| 70 | + * @param $salt String |
| 71 | + * @return Bool, or null if no session cookie is set |
| 72 | + */ |
| 73 | + public static function match( $val=null, $action='edit', $salt='' ){ |
| 74 | + $action = ucfirst( $action ); |
| 75 | + |
| 76 | + global $wgUser; |
| 77 | + if( $action == 'edit' && $wgUser->isAnon() ){ |
| 78 | + return $val === self::TOKEN_SUFFIX; |
| 79 | + } |
| 80 | + |
| 81 | + if( !self::has( $action ) ){ |
| 82 | + return null; |
| 83 | + } |
| 84 | + |
| 85 | + if( $val === null ){ |
| 86 | + global $wgRequest; |
| 87 | + $val = $wgRequest->getText( "wp{$action}Token" ); |
| 88 | + } |
| 89 | + |
| 90 | + return md5( self::get( $action ) . $salt ) . self::TOKEN_SUFFIX === $val; |
| 91 | + } |
| 92 | + |
| 93 | + /** |
| 94 | + * Whether a token is set for the given action |
| 95 | + * @return Bool |
| 96 | + */ |
| 97 | + public static function has( $action='edit' ){ |
| 98 | + return self::get( $action ) !== null; |
| 99 | + } |
| 100 | + |
| 101 | + /** |
| 102 | + * Get the token set for a given action |
| 103 | + */ |
| 104 | + public static function get( $action='edit' ){ |
| 105 | + global $wgRequest; |
| 106 | + $action = ucfirst( $action ); |
| 107 | + return $wgRequest->getSessionData( "ws{$action}Token" ); |
| 108 | + } |
| 109 | + |
| 110 | + /** |
| 111 | + * Set the given token for the given action in the session |
| 112 | + * @param $token String |
| 113 | + * @param $action String |
| 114 | + */ |
| 115 | + public static function store( $token, $action='edit' ){ |
| 116 | + global $wgRequest; |
| 117 | + $action = ucfirst( $action ); |
| 118 | + $wgRequest->setSessionData( "ws{$action}Token", $token ); |
| 119 | + } |
| 120 | + |
| 121 | + /** |
| 122 | + * Delete the token after use |
| 123 | + */ |
| 124 | + public static function clear( $action='edit' ){ |
| 125 | + self::set( null, $action ); |
| 126 | + } |
| 127 | + |
| 128 | + /** |
| 129 | + * Generate a random token |
| 130 | + * |
| 131 | + * @param $salt String Optional salt value |
| 132 | + * @return String 32-char random token |
| 133 | + */ |
| 134 | + public static function generate( $salt = '' ) { |
| 135 | + $rand = dechex( mt_rand() ) . dechex( mt_rand() ); |
| 136 | + return md5( $rand . $salt ); |
| 137 | + } |
| 138 | +} |
Property changes on: branches/happy-melon/phase3/includes/Token.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 139 | + native |
Index: branches/happy-melon/phase3/includes/HTMLForm.php |
— | — | @@ -79,6 +79,7 @@ |
80 | 80 | public $mFieldData; |
81 | 81 | |
82 | 82 | protected $mSubmitCallback; |
| 83 | + protected $mFilterCallback; |
83 | 84 | protected $mValidationErrorMessage; |
84 | 85 | |
85 | 86 | protected $mPre = ''; |
— | — | @@ -97,6 +98,7 @@ |
98 | 99 | protected $mButtons = array(); |
99 | 100 | |
100 | 101 | protected $mWrapperLegend = false; |
| 102 | + protected $mTokenAction = 'Edit'; |
101 | 103 | |
102 | 104 | /** |
103 | 105 | * Build a new HTMLForm from an array of field attributes |
— | — | @@ -186,7 +188,11 @@ |
187 | 189 | * @return Bool whether submission was successful. |
188 | 190 | */ |
189 | 191 | function show() { |
190 | | - $html = ''; |
| 192 | + global $wgRequest; |
| 193 | + // Check if we have the info we need |
| 194 | + if ( ! $this->mTitle ) { |
| 195 | + throw new MWException( "You must call setTitle() on an HTMLForm" ); |
| 196 | + } |
191 | 197 | |
192 | 198 | self::addJS(); |
193 | 199 | |
— | — | @@ -194,11 +200,8 @@ |
195 | 201 | $this->loadData(); |
196 | 202 | |
197 | 203 | # Try a submission |
198 | | - global $wgUser, $wgRequest; |
199 | | - $editToken = $wgRequest->getVal( 'wpEditToken' ); |
200 | | - |
201 | 204 | $result = false; |
202 | | - if ( $wgUser->matchEditToken( $editToken ) ) |
| 205 | + if( $wgRequest->wasPosted() ){ |
203 | 206 | $result = $this->trySubmit(); |
204 | 207 | |
205 | 208 | if( $result === true ) |
— | — | @@ -217,6 +220,11 @@ |
218 | 221 | * display. |
219 | 222 | */ |
220 | 223 | function trySubmit() { |
| 224 | + # Check the session tokens |
| 225 | + if ( !Token::match( null, $this->mTokenAction ) ) { |
| 226 | + return array( 'sessionfailure' ); |
| 227 | + } |
| 228 | + |
221 | 229 | # Check for validation |
222 | 230 | foreach( $this->mFlatFields as $fieldname => $field ) { |
223 | 231 | if ( !empty( $field->mParams['nodata'] ) ) |
— | — | @@ -362,8 +370,11 @@ |
363 | 371 | function getHiddenFields() { |
364 | 372 | global $wgUser; |
365 | 373 | $html = ''; |
366 | | - |
367 | | - $html .= Html::hidden( 'wpEditToken', $wgUser->editToken(), array( 'id' => 'wpEditToken' ) ) . "\n"; |
| 374 | + $html .= Html::hidden( |
| 375 | + "wp{$this->mTokenAction}Token", |
| 376 | + Token::set( $this->mTokenAction ), |
| 377 | + array( 'id' => 'wpEditToken' ) |
| 378 | + ) . "\n"; |
368 | 379 | $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n"; |
369 | 380 | |
370 | 381 | foreach( $this->mHiddenFields as $name => $value ){ |
— | — | @@ -526,6 +537,16 @@ |
527 | 538 | function setMessagePrefix( $p ) { |
528 | 539 | $this->mMessagePrefix = $p; |
529 | 540 | } |
| 541 | + |
| 542 | + /** |
| 543 | + * If you want to protect the form from CSRF by a token other than |
| 544 | + * the usual wsEditToken, set something here. |
| 545 | + * @see Token::set() |
| 546 | + * @param $a |
| 547 | + */ |
| 548 | + function setTokenAction( $a ){ |
| 549 | + $this->mTokenAction = ucfirst( $a ); |
| 550 | + } |
530 | 551 | |
531 | 552 | /** |
532 | 553 | * Set the title for form submission |
— | — | @@ -626,8 +647,19 @@ |
627 | 648 | * @return unknown_type |
628 | 649 | */ |
629 | 650 | function filterDataForSubmit( $data ) { |
| 651 | + if( is_callable( $this->mFilterCallback ) ){ |
| 652 | + $data = call_user_func( $this->mFilterCallback, $data ); |
| 653 | + } |
630 | 654 | return $data; |
631 | 655 | } |
| 656 | + |
| 657 | + /** |
| 658 | + * Set a filter callback. |
| 659 | + * @param $function Callback |
| 660 | + */ |
| 661 | + public function setFilterCallback( $callback ){ |
| 662 | + $this->mFilterCallback = $callback; |
| 663 | + } |
632 | 664 | } |
633 | 665 | |
634 | 666 | /** |
Index: branches/happy-melon/phase3/includes/api/ApiLogin.php |
— | — | @@ -56,68 +56,92 @@ |
57 | 57 | |
58 | 58 | $result = array (); |
59 | 59 | |
60 | | - $req = new FauxRequest(array ( |
61 | | - 'wpName' => $params['name'], |
62 | | - 'wpPassword' => $params['password'], |
63 | | - 'wpDomain' => $params['domain'], |
64 | | - 'wpRemember' => '' |
65 | | - )); |
66 | | - |
| 60 | + $req = array( |
| 61 | + 'Name' => $params['name'], |
| 62 | + 'Password' => $params['password'], |
| 63 | + 'Domain' => $params['domain'], |
| 64 | + 'Token' => $params['token'], |
| 65 | + 'Remember' => '' |
| 66 | + ); |
67 | 67 | // Init session if necessary |
68 | 68 | if( session_id() == '' ) { |
69 | 69 | wfSetupSession(); |
70 | 70 | } |
71 | | - |
72 | | - $loginForm = new LoginForm($req); |
73 | | - switch ($authRes = $loginForm->authenticateUserData()) { |
74 | | - case LoginForm :: SUCCESS : |
75 | | - global $wgUser, $wgCookiePrefix; |
76 | | - |
77 | | - $wgUser->setOption('rememberpassword', 1); |
78 | | - $wgUser->setCookies(); |
79 | | - |
80 | | - // Run hooks. FIXME: split back and frontend from this hook. |
81 | | - // FIXME: This hook should be placed in the backend |
82 | | - $injected_html = ''; |
83 | | - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html)); |
84 | | - |
85 | | - $result['result'] = 'Success'; |
86 | | - $result['lguserid'] = intval($wgUser->getId()); |
87 | | - $result['lgusername'] = $wgUser->getName(); |
88 | | - $result['lgtoken'] = $wgUser->getToken(); |
89 | | - $result['cookieprefix'] = $wgCookiePrefix; |
90 | | - $result['sessionid'] = session_id(); |
91 | | - break; |
92 | | - |
93 | | - case LoginForm :: NO_NAME : |
94 | | - $result['result'] = 'NoName'; |
95 | | - break; |
96 | | - case LoginForm :: ILLEGAL : |
97 | | - $result['result'] = 'Illegal'; |
98 | | - break; |
99 | | - case LoginForm :: WRONG_PLUGIN_PASS : |
100 | | - $result['result'] = 'WrongPluginPass'; |
101 | | - break; |
102 | | - case LoginForm :: NOT_EXISTS : |
103 | | - $result['result'] = 'NotExists'; |
104 | | - break; |
105 | | - case LoginForm :: WRONG_PASS : |
106 | | - $result['result'] = 'WrongPass'; |
107 | | - break; |
108 | | - case LoginForm :: EMPTY_PASS : |
109 | | - $result['result'] = 'EmptyPass'; |
110 | | - break; |
111 | | - case LoginForm :: CREATE_BLOCKED : |
112 | | - $result['result'] = 'CreateBlocked'; |
113 | | - $result['details'] = 'Your IP address is blocked from account creation'; |
114 | | - break; |
115 | | - case LoginForm :: THROTTLED : |
116 | | - global $wgPasswordAttemptThrottle; |
117 | | - $result['result'] = 'Throttled'; |
118 | | - $result['wait'] = intval($wgPasswordAttemptThrottle['seconds']); |
119 | | - break; |
120 | | - default : |
121 | | - ApiBase :: dieDebug(__METHOD__, "Unhandled case value: {$authRes}"); |
| 71 | + if( $params['token'] |
| 72 | + && !Token::match( $params['token'], 'login' ) ) |
| 73 | + { |
| 74 | + $result['result'] = 'WrongToken'; |
| 75 | + |
| 76 | + } elseif ( !Token::has( 'login' ) || !$params['token'] |
| 77 | + || !Token::match( $params['token'], 'login' ) ) |
| 78 | + { |
| 79 | + global $wgCookiePrefix; |
| 80 | + $result['result'] = 'NeedToken'; |
| 81 | + $result['token'] = Token::set( 'login' ); |
| 82 | + $result['cookieprefix'] = $wgCookiePrefix; |
| 83 | + $result['sessionid'] = session_id(); |
| 84 | + |
| 85 | + } else { |
| 86 | + $login = new Login( $req ); |
| 87 | + switch ( $authRes = $login->attemptLogin() ) { |
| 88 | + case Login::SUCCESS: |
| 89 | + Token::clear( 'login' ); |
| 90 | + global $wgUser, $wgCookiePrefix; |
| 91 | + |
| 92 | + $result['result'] = 'Success'; |
| 93 | + $result['lguserid'] = intval( $wgUser->getId() ); |
| 94 | + $result['lgusername'] = $wgUser->getName(); |
| 95 | + $result['lgtoken'] = $wgUser->getToken(); |
| 96 | + $result['cookieprefix'] = $wgCookiePrefix; |
| 97 | + $result['sessionid'] = session_id(); |
| 98 | + break; |
| 99 | + |
| 100 | + case Login::NO_NAME: |
| 101 | + $result['result'] = 'NoName'; |
| 102 | + break; |
| 103 | + |
| 104 | + case Login::ILLEGAL: |
| 105 | + $result['result'] = 'Illegal'; |
| 106 | + break; |
| 107 | + |
| 108 | + case Login::WRONG_PLUGIN_PASS: |
| 109 | + $result['result'] = 'WrongPluginPass'; |
| 110 | + break; |
| 111 | + |
| 112 | + case Login::NOT_EXISTS: |
| 113 | + $result['result'] = 'NotExists'; |
| 114 | + break; |
| 115 | + |
| 116 | + # bug 20223 - Treat a temporary password as wrong. Per |
| 117 | + # SpecialUserLogin - "The e-mailed temporary password |
| 118 | + # should not be used for actual logins" |
| 119 | + case Login::RESET_PASS: |
| 120 | + case Login::WRONG_PASS: |
| 121 | + $result['result'] = 'WrongPass'; |
| 122 | + break; |
| 123 | + |
| 124 | + case Login::EMPTY_PASS: |
| 125 | + $result['result'] = 'EmptyPass'; |
| 126 | + break; |
| 127 | + |
| 128 | + case Login::CREATE_BLOCKED: |
| 129 | + $result['result'] = 'CreateBlocked'; |
| 130 | + $result['details'] = 'Your IP address is blocked from account creation'; |
| 131 | + break; |
| 132 | + |
| 133 | + case Login::THROTTLED: |
| 134 | + global $wgPasswordAttemptThrottle; |
| 135 | + $result['result'] = 'Throttled'; |
| 136 | + $result['wait'] = intval( $wgPasswordAttemptThrottle['seconds'] ); |
| 137 | + break; |
| 138 | + |
| 139 | + case Login::USER_BLOCKED: |
| 140 | + $result['result'] = 'Blocked'; |
| 141 | + break; |
| 142 | + |
| 143 | + default: |
| 144 | + ApiBase::dieDebug( __METHOD__, "Unhandled case value: {$authRes}" ); |
| 145 | + } |
122 | 146 | } |
123 | 147 | |
124 | 148 | $this->getResult()->addValue(null, 'login', $result); |
Index: branches/happy-melon/phase3/includes/Login.php |
— | — | @@ -15,8 +15,9 @@ |
16 | 16 | const RESET_PASS = 7; |
17 | 17 | const ABORTED = 8; |
18 | 18 | const THROTTLED = 10; |
19 | | - const FAILED = 11; |
20 | | - const READ_ONLY = 12; |
| 19 | + const USER_BLOCKED = 11; |
| 20 | + const FAILED = 12; |
| 21 | + const READ_ONLY = 13; |
21 | 22 | |
22 | 23 | const MAIL_PASSCHANGE_FORBIDDEN = 21; |
23 | 24 | const MAIL_BLOCKED = 22; |
— | — | @@ -52,29 +53,33 @@ |
53 | 54 | |
54 | 55 | /** |
55 | 56 | * Constructor |
56 | | - * @param WebRequest $request A WebRequest object passed by reference. |
57 | | - * uses $wgRequest if not given. |
| 57 | + * @param $data Array data object passed by reference. |
58 | 58 | */ |
59 | | - public function __construct( &$request=null ) { |
60 | | - global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin; |
61 | | - if( !$request ) $request = &$wgRequest; |
| 59 | + public function __construct( $data ) { |
| 60 | + global $wgAuth, $wgHiddenPrefs, $wgEnableEmail; |
62 | 61 | |
63 | | - $this->mName = $request->getText( 'wpName' ); |
64 | | - $this->mPassword = $request->getText( 'wpPassword' ); |
65 | | - $this->mDomain = $request->getText( 'wpDomain' ); |
66 | | - $this->mRemember = $request->getCheck( 'wpRemember' ) ? 1 : 0; |
| 62 | + $this->mName = $data['Name']; |
| 63 | + $this->mPassword = $data['Password']; |
| 64 | + |
| 65 | + if( isset( $data['Remember'] ) ){ |
| 66 | + $this->mRemember = $data['Remember'] ? 1 : 0; |
| 67 | + } |
67 | 68 | |
68 | | - if( $wgEnableEmail ) { |
69 | | - $this->mEmail = $request->getText( 'wpEmail' ); |
| 69 | + if( $wgEnableEmail && isset( $data['Email']) ) { |
| 70 | + $this->mEmail = $data['Email']; |
70 | 71 | } else { |
71 | 72 | $this->mEmail = ''; |
72 | 73 | } |
73 | | - if( !in_array( 'realname', $wgHiddenPrefs ) ) { |
74 | | - $this->mRealName = $request->getText( 'wpRealName' ); |
| 74 | + |
| 75 | + if( !in_array( 'realname', $wgHiddenPrefs ) && isset( $data['RealName'] ) ) { |
| 76 | + $this->mRealName = $data['RealName']; |
75 | 77 | } else { |
76 | 78 | $this->mRealName = ''; |
77 | 79 | } |
78 | 80 | |
| 81 | + $this->mDomain = isset( $data['Domain'] ) |
| 82 | + ? $data['Domain'] |
| 83 | + : ''; |
79 | 84 | if( !$wgAuth->validDomain( $this->mDomain ) ) { |
80 | 85 | $this->mDomain = 'invaliddomain'; |
81 | 86 | } |
— | — | @@ -90,7 +95,8 @@ |
91 | 96 | * foreign database; in the latter case, a local user record may |
92 | 97 | * or may not be created and initialised. |
93 | 98 | * @param $password String if set, log in with this password rather |
94 | | - * than whatever was given in the WebRequest. |
| 99 | + * than whatever was given in the initial config. If set to |
| 100 | + * Bool true, password always matches |
95 | 101 | * @return a Login class constant representing the status. |
96 | 102 | */ |
97 | 103 | public function attemptLogin( $password=null ){ |
— | — | @@ -210,8 +216,9 @@ |
211 | 217 | } |
212 | 218 | |
213 | 219 | # Initialise $this->mUser |
214 | | - if( $this->getUser() === null ) |
| 220 | + if( $this->getUser() === null ){ |
215 | 221 | return $this->mUserCode; |
| 222 | + } |
216 | 223 | |
217 | 224 | # Shortcut |
218 | 225 | if( $wgUser->getName() === $this->mUser->getName() ){ |
— | — | @@ -224,7 +231,26 @@ |
225 | 232 | return self::ABORTED; |
226 | 233 | } |
227 | 234 | |
228 | | - if( !$this->mUser->checkPassword( $this->mPassword ) ) { |
| 235 | + if( $this->mUser->checkPassword( $this->mPassword ) || $this->mPassword === true ) { |
| 236 | + # If we've enabled it, make it so that a blocked user cannot login |
| 237 | + # This is only reached if the password matches, preventing fishing |
| 238 | + # for blocked accounts on private wikis. |
| 239 | + global $wgBlockDisablesLogin; |
| 240 | + if( $wgBlockDisablesLogin && $this->mUser->isBlocked() ) { |
| 241 | + $this->mLoginResult = 'login-userblocked'; |
| 242 | + return self::USER_BLOCKED; |
| 243 | + } |
| 244 | + |
| 245 | + $wgAuth->updateUser( $this->mUser ); |
| 246 | + $wgUser = $this->mUser; |
| 247 | + |
| 248 | + # Reset throttle after a successful login |
| 249 | + if( $throttleCount ) { |
| 250 | + $wgMemc->delete( $throttleKey ); |
| 251 | + } |
| 252 | + |
| 253 | + $retval = self::SUCCESS; |
| 254 | + } else { |
229 | 255 | if( $this->mUser->checkTemporaryPassword( $this->mPassword ) ) { |
230 | 256 | # The e-mailed temporary password should not be used for actual |
231 | 257 | # logins; that's a very sloppy habit, and insecure if an |
— | — | @@ -259,16 +285,6 @@ |
260 | 286 | $this->mLoginResult = 'wrongpassword'; |
261 | 287 | } |
262 | 288 | } |
263 | | - } else { |
264 | | - $wgAuth->updateUser( $this->mUser ); |
265 | | - $wgUser = $this->mUser; |
266 | | - |
267 | | - # Reset throttle after a successful login |
268 | | - if( $throttleCount ) { |
269 | | - $wgMemc->delete( $throttleKey ); |
270 | | - } |
271 | | - |
272 | | - $retval = self::SUCCESS; |
273 | 289 | } |
274 | 290 | wfRunHooks( 'LoginAuthenticateAudit', array( &$this->mUser, $this->mPassword, $retval ) ); |
275 | 291 | return $retval; |
— | — | @@ -281,8 +297,9 @@ |
282 | 298 | * @return &User, or &null on failure. Sets a status code in $this->mUserCode |
283 | 299 | */ |
284 | 300 | public function &getUser(){ |
285 | | - if( $this->mUserCode !== null ) |
| 301 | + if( $this->mUserCode !== null ){ |
286 | 302 | return $this->mUser; |
| 303 | + } |
287 | 304 | |
288 | 305 | # Unstub $wgUser if it's not already by calling getName(). This calls |
289 | 306 | # the UserLoadFromSession hook, which potentially creates the user in |
— | — | @@ -309,15 +326,16 @@ |
310 | 327 | |
311 | 328 | # TODO: Allow some magic here for invalid external names, e.g., let the |
312 | 329 | # user choose a different wiki name. |
313 | | - if( is_null( $this->mUser ) || !User::isUsableName( $this->mUser->getName() ) ) { |
| 330 | + if( !$this->mUser instanceof User || !User::isUsableName( $this->mUser->getName() ) ) { |
314 | 331 | $this->mUserCode = self::ILLEGAL; |
315 | 332 | $this->mUser = null; |
316 | 333 | return $this->mUser; |
317 | 334 | } |
318 | 335 | |
| 336 | + |
319 | 337 | # If the user doesn't exist in the local database, our only chance |
320 | 338 | # is for an external auth plugin to autocreate the local user first. |
321 | | - if( $this->mUser->getID() == 0 ) { |
| 339 | + if( $this->mUser->getId() == 0 ) { |
322 | 340 | if( $this->canAutoCreate() == self::SUCCESS ) { |
323 | 341 | |
324 | 342 | wfDebug( __METHOD__.": creating account\n" ); |
— | — | @@ -344,6 +362,15 @@ |
345 | 363 | return $this->mUser; |
346 | 364 | } |
347 | 365 | } else { |
| 366 | + global $wgAutocreatePolicy; |
| 367 | + if ( $this->mExtUser |
| 368 | + && $wgAutocreatePolicy != 'never' |
| 369 | + && $this->mExtUser->authenticate( $this->mPassword ) ) |
| 370 | + { |
| 371 | + # The external user and local user have the same name and |
| 372 | + # password, so we assume they're the same. |
| 373 | + $this->mExtUser->linkToLocal( $this->mUser->getID() ); |
| 374 | + } |
348 | 375 | $this->mUser->load(); |
349 | 376 | } |
350 | 377 | |
— | — | @@ -369,6 +396,7 @@ |
370 | 397 | 'name' => User::getCanonicalName( $this->mName ), |
371 | 398 | 'password' => $byEmail ? null : User::crypt( $this->mPassword ), |
372 | 399 | 'email' => $this->mEmail, |
| 400 | + 'real_name' => $this->mRealName, |
373 | 401 | 'options' => array( |
374 | 402 | 'rememberpassword' => $this->mRemember ? 1 : 0, |
375 | 403 | ), |
— | — | @@ -385,7 +413,7 @@ |
386 | 414 | |
387 | 415 | # Or new ExternalUser plugins |
388 | 416 | if( $this->mExtUser ) { |
389 | | - $this->mExtUser->link( $this->mUser->getId() ); |
| 417 | + $this->mExtUser->linkToLocal( $this->mUser->getId() ); |
390 | 418 | $email = $this->mExtUser->getPref( 'emailaddress' ); |
391 | 419 | if( $email && !$this->mEmail ) { |
392 | 420 | $this->mUser->setEmail( $email ); |
— | — | @@ -395,13 +423,14 @@ |
396 | 424 | # Update user count and newuser logs |
397 | 425 | $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 ); |
398 | 426 | $ssUpdate->doUpdate(); |
399 | | - if( $autocreate ) |
| 427 | + if( $autocreate ){ |
400 | 428 | $this->mUser->addNewUserLogEntryAutoCreate(); |
401 | | - elseif( $wgUser->isAnon() ) |
| 429 | + } elseif ( $wgUser->isAnon() ){ |
402 | 430 | # Avoid spamming IP addresses all over the newuser log |
403 | 431 | $this->mUser->addNewUserLogEntry( $this->mUser, $byEmail ); |
404 | | - else |
| 432 | + } else { |
405 | 433 | $this->mUser->addNewUserLogEntry( $wgUser, $byEmail ); |
| 434 | + } |
406 | 435 | |
407 | 436 | # Run hooks |
408 | 437 | wfRunHooks( 'AddNewAccount', array( &$this->mUser, $autocreate, $byEmail ) ); |
— | — | @@ -421,13 +450,13 @@ |
422 | 451 | */ |
423 | 452 | public function attemptCreation( $byEmail=false ) { |
424 | 453 | global $wgUser, $wgOut; |
425 | | - global $wgEnableSorbs, $wgProxyWhitelist; |
426 | 454 | global $wgMemc, $wgAccountCreationThrottle; |
427 | 455 | global $wgAuth; |
428 | 456 | global $wgEmailAuthentication, $wgEmailConfirmToEdit; |
429 | 457 | |
430 | | - if( wfReadOnly() ) |
| 458 | + if( wfReadOnly() ) { |
431 | 459 | return self::READ_ONLY; |
| 460 | + } |
432 | 461 | |
433 | 462 | # If the user passes an invalid domain, something is fishy |
434 | 463 | if( !$wgAuth->validDomain( $this->mDomain ) ) { |
— | — | @@ -451,9 +480,7 @@ |
452 | 481 | } |
453 | 482 | |
454 | 483 | $ip = wfGetIP(); |
455 | | - if( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) && |
456 | | - $wgUser->inSorbsBlacklist( $ip ) ) |
457 | | - { |
| 484 | + if( $wgUser->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) { |
458 | 485 | $this->mCreateResult = 'sorbs_create_account_reason'; |
459 | 486 | return self::CREATE_SORBS; |
460 | 487 | } |
— | — | @@ -474,7 +501,7 @@ |
475 | 502 | # Check that the password is acceptable, if we're actually |
476 | 503 | # going to use it |
477 | 504 | if( !$byEmail ){ |
478 | | - $valid = $this->mUser->isValidPassword( $this->mPassword ); |
| 505 | + $valid = $this->mUser->getPasswordValidity( $this->mPassword ); |
479 | 506 | if( $valid !== true ) { |
480 | 507 | $this->mCreateResult = $valid; |
481 | 508 | return self::CREATE_BADPASS; |
— | — | @@ -524,10 +551,11 @@ |
525 | 552 | } |
526 | 553 | |
527 | 554 | $result = $this->initUser( false, $byEmail ); |
528 | | - if( $result === null ) |
| 555 | + if( $result === null ){ |
529 | 556 | # It's unlikely we'd get here without some exception |
530 | 557 | # being thrown, but it's probably possible... |
531 | 558 | return self::FAILED; |
| 559 | + } |
532 | 560 | |
533 | 561 | if( $byEmail ){ |
534 | 562 | # Send out the password by email |
— | — | @@ -562,62 +590,94 @@ |
563 | 591 | public function mailPassword( $text='passwordremindertext', $title='passwordremindertitle' ) { |
564 | 592 | global $wgUser, $wgOut, $wgAuth, $wgServer, $wgScript, $wgNewPasswordExpiry; |
565 | 593 | |
566 | | - if( wfReadOnly() ) |
| 594 | + if( wfReadOnly() ) { |
567 | 595 | return self::READ_ONLY; |
| 596 | + } |
568 | 597 | |
569 | 598 | # If we let the email go out, it will take users to a form where |
570 | 599 | # they are forced to change their password, so don't let us go |
571 | 600 | # there if we don't want passwords changed. |
572 | | - if( !$wgAuth->allowPasswordChange() ) |
| 601 | + if( !$wgAuth->allowPasswordChange() ) { |
573 | 602 | return self::MAIL_PASSCHANGE_FORBIDDEN; |
| 603 | + } |
574 | 604 | |
575 | 605 | # Check against blocked IPs |
576 | 606 | # FIXME: -- should we not? |
577 | | - if( $wgUser->isBlocked() ) |
| 607 | + if( $wgUser->isBlocked() ){ |
578 | 608 | return self::MAIL_BLOCKED; |
| 609 | + } |
579 | 610 | |
580 | 611 | # Check for hooks |
581 | | - if( !wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$this->mMailResult ) ) ) |
| 612 | + if( !wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$this->mMailResult ) ) ){ |
582 | 613 | return self::ABORTED; |
| 614 | + } |
583 | 615 | |
584 | 616 | # Check against the rate limiter |
585 | | - if( $wgUser->pingLimiter( 'mailpassword' ) ) |
| 617 | + if( $wgUser->pingLimiter( 'mailpassword' ) ){ |
586 | 618 | return self::MAIL_PING_THROTTLED; |
| 619 | + } |
587 | 620 | |
588 | 621 | # Initialise the user before checking data about them |
589 | | - if( is_null( $this->getUser() ) ) |
| 622 | + if( is_null( $this->getUser() ) ){ |
590 | 623 | return self::NO_NAME; |
| 624 | + } |
591 | 625 | |
592 | 626 | # And that the resulting user actually exists |
593 | | - if( $this->mUser->getId() === 0 ) |
| 627 | + if( $this->mUser->getId() === 0 ){ |
594 | 628 | return self::NOT_EXISTS; |
| 629 | + } |
595 | 630 | |
596 | 631 | # Check against password throttle |
597 | | - if( $this->mUser->isPasswordReminderThrottled() ) |
| 632 | + if( $this->mUser->isPasswordReminderThrottled() ){ |
598 | 633 | return self::MAIL_PASS_THROTTLED; |
| 634 | + } |
599 | 635 | |
600 | 636 | # User doesn't have email address set |
601 | | - if( $this->mUser->getEmail() === '' ) |
| 637 | + if( $this->mUser->getEmail() === '' ){ |
602 | 638 | return self::MAIL_EMPTY_EMAIL; |
| 639 | + } |
603 | 640 | |
604 | 641 | # Don't send to people who are acting fishily by hiding their IP |
605 | 642 | $ip = wfGetIP(); |
606 | | - if( !$ip ) |
| 643 | + if( !$ip ){ |
607 | 644 | return self::MAIL_BAD_IP; |
| 645 | + } |
| 646 | + |
| 647 | + # If blocked users cannot log in, don't let them reset passwords either. |
| 648 | + global $wgBlockDisablesLogin; |
| 649 | + if( $wgBlockDisablesLogin && $this->mUser->isBlocked() ) { |
| 650 | + return self::USER_BLOCKED; |
| 651 | + } |
608 | 652 | |
609 | 653 | # Let hooks do things with the data |
610 | 654 | wfRunHooks( 'User::mailPasswordInternal', array( &$wgUser, &$ip, &$this->mUser) ); |
611 | 655 | |
612 | 656 | $newpass = $this->mUser->randomPassword(); |
613 | 657 | $this->mUser->setNewpassword( $newpass, true ); |
614 | | - $this->mUser->saveSettings(); |
615 | 658 | |
616 | | - $message = wfMsgExt( $text, array( 'parsemag' ), $ip, $this->mUser->getName(), $newpass, |
617 | | - $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) ); |
618 | | - $this->mMailResult = $this->mUser->sendMail( wfMsg( $title ), $message ); |
| 659 | + $message = wfMsgExt( |
| 660 | + $text, |
| 661 | + array( 'parsemag', 'language' => $this->mUser->getOption('language') ), |
| 662 | + array( |
| 663 | + $ip, |
| 664 | + $this->mUser->getName(), |
| 665 | + $newpass, |
| 666 | + $wgServer . $wgScript, |
| 667 | + round( $wgNewPasswordExpiry / 86400 ) |
| 668 | + ) |
| 669 | + ); |
| 670 | + $title = wfMsgExt( |
| 671 | + $title, |
| 672 | + array( 'parseinline', 'language' => $this->mUser->getOption('language') ) |
| 673 | + ); |
| 674 | + $this->mMailResult = $this->mUser->sendMail( $title, $message ); |
619 | 675 | if( WikiError::isError( $this->mMailResult ) ) { |
| 676 | + # Discard the new password by not saving it, and hence |
| 677 | + # also not setting the newpassword throttle |
620 | 678 | return self::MAIL_ERROR; |
621 | 679 | } else { |
| 680 | + # Save the new password |
| 681 | + $this->mUser->saveSettings(); |
622 | 682 | return self::SUCCESS; |
623 | 683 | } |
624 | 684 | } |
Index: branches/happy-melon/phase3/includes/specials/SpecialUserlogin.php |
— | — | @@ -6,14 +6,11 @@ |
7 | 7 | |
8 | 8 | class SpecialUserLogin extends SpecialPage { |
9 | 9 | |
10 | | - var $mUsername, $mPassword, $mReturnTo, $mCookieCheck, $mPosted; |
11 | | - var $mCreateaccount, $mCreateaccountMail, $mMailmypassword; |
12 | | - var $mRemember, $mDomain, $mLanguage; |
13 | | - var $mSkipCookieCheck, $mReturnToQuery; |
| 10 | + var $mReturnTo, $mReturnToQuery; |
14 | 11 | |
15 | | - public $mDomains = array(); |
16 | | - |
| 12 | + public $mDomains = array(); # set by hooks |
17 | 13 | public $mFormHeader = ''; # Can be filled by hooks etc |
| 14 | + |
18 | 15 | public $mFormFields = array( |
19 | 16 | 'Name' => array( |
20 | 17 | 'type' => 'text', |
— | — | @@ -38,87 +35,71 @@ |
39 | 36 | ), |
40 | 37 | 'Remember' => array( |
41 | 38 | 'type' => 'check', |
42 | | - 'label-message' => 'remembermypassword', |
| 39 | + 'label' => ''/* set in constructor */, |
43 | 40 | 'id' => 'wpRemember', |
44 | 41 | ) |
45 | 42 | ); |
46 | 43 | |
47 | | - protected $mLogin; # Login object |
48 | | - |
49 | 44 | public function __construct(){ |
50 | 45 | parent::__construct( 'Userlogin' ); |
| 46 | + |
| 47 | + global $wgLang, $wgCookieExpiration, $wgRequest, $wgRedirectOnLogin; |
| 48 | + |
| 49 | + $this->mFormFields['Remember']['label'] = wfMsgExt( |
| 50 | + 'remembermypassword', |
| 51 | + 'parseinline', |
| 52 | + $wgLang->formatNum( ceil( $wgCookieExpiration / 86400 ) ) |
| 53 | + ); |
| 54 | + |
| 55 | + $this->mReturnTo = $wgRequest->getVal( 'returnto' ); |
| 56 | + $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' ); |
| 57 | + |
| 58 | + if ( $wgRedirectOnLogin ) { |
| 59 | + $this->mReturnTo = $wgRedirectOnLogin; |
| 60 | + $this->mReturnToQuery = ''; |
| 61 | + } |
| 62 | + |
| 63 | + # When switching accounts, it sucks to get automatically logged out |
| 64 | + $returnToTitle = Title::newFromText( $this->mReturnTo ); |
| 65 | + if( $returnToTitle instanceof Title && $returnToTitle->isSpecial( 'Userlogout' ) ) { |
| 66 | + $this->mReturnTo = ''; |
| 67 | + $this->mReturnToQuery = ''; |
| 68 | + } |
51 | 69 | } |
52 | 70 | |
53 | 71 | function execute( $par ) { |
54 | | - global $wgRequest; |
| 72 | + global $wgRequest, $wgOut; |
55 | 73 | |
56 | | - # Redirect out for account creation, for B/C |
57 | | - $type = ( $par == 'signup' ) ? $par : $wgRequest->getText( 'type' ); |
58 | | - if( $type == 'signup' ){ |
| 74 | + # Pre-1.17, CreateAccount was at Special:UserLogin/signup |
| 75 | + if( $par == 'signup' || $wgRequest->getText( 'type' ) == 'signup' ){ |
59 | 76 | $sp = new SpecialCreateAccount(); |
60 | 77 | $sp->execute( $par ); |
61 | 78 | return; |
62 | 79 | } |
63 | 80 | |
64 | | - # Because we're transitioning from logged-out, who might not |
65 | | - # have a session, to logged-in, who always do, we need to make |
66 | | - # sure that we *always* have a session... |
67 | | - if( session_id() == '' ) { |
68 | | - wfSetupSession(); |
69 | | - } |
| 81 | + $wgOut->setPageTitle( wfMsg( 'login' ) ); |
| 82 | + $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
| 83 | + $wgOut->setArticleRelated( false ); |
| 84 | + $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords |
70 | 85 | |
71 | | - $this->loadQuery(); |
72 | | - $this->mLogin = new Login(); |
73 | | - |
74 | | - if ( $wgRequest->getCheck( 'wpCookieCheck' ) ) { |
75 | | - $this->onCookieRedirectCheck(); |
76 | | - return; |
77 | | - } else if( $wgRequest->wasPosted() ) { |
78 | | - if ( $this->mMailmypassword ) { |
79 | | - return $this->showMailPage(); |
80 | | - } else { |
81 | | - return $this->processLogin(); |
82 | | - } |
83 | | - } else { |
84 | | - $this->mainLoginForm( '' ); |
85 | | - } |
| 86 | + $form = $this->getForm(); |
| 87 | + |
| 88 | + $form->show(); |
86 | 89 | } |
87 | | - |
88 | | - /** |
89 | | - * Load member variables from the HTTP request data |
90 | | - * @param $par String the fragment passed to execute() |
91 | | - */ |
92 | | - protected function loadQuery(){ |
93 | | - global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin; |
94 | | - |
95 | | - $this->mUsername = $wgRequest->getText( 'wpName' ); |
96 | | - $this->mPassword = $wgRequest->getText( 'wpPassword' ); |
97 | | - $this->mDomain = $wgRequest->getText( 'wpDomain' ); |
98 | | - $this->mLanguage = $wgRequest->getText( 'uselang' ); |
99 | | - |
100 | | - $this->mReturnTo = $wgRequest->getVal( 'returnto' ); |
101 | | - $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' ); |
102 | | - |
103 | | - $this->mMailmypassword = $wgRequest->getCheck( 'wpMailmypassword' ) |
104 | | - && $wgEnableEmail; |
105 | | - $this->mRemember = $wgRequest->getCheck( 'wpRemember' ); |
106 | | - $this->mSkipCookieCheck = $wgRequest->getCheck( 'wpSkipCookieCheck' ); |
107 | | - |
108 | | - if( !$wgAuth->validDomain( $this->mDomain ) ) { |
109 | | - $this->mDomain = 'invaliddomain'; |
110 | | - } |
111 | | - $wgAuth->setDomain( $this->mDomain ); |
112 | 90 | |
113 | | - if ( $wgRedirectOnLogin ) { |
114 | | - $this->mReturnTo = $wgRedirectOnLogin; |
115 | | - $this->mReturnToQuery = ''; |
| 91 | + public function formFilterCallback( $data ){ |
| 92 | + global $wgRequest, $wgEnableEmail; |
| 93 | + $data['mailpassword'] = $wgRequest->getCheck( 'wpMailmypassword' ) |
| 94 | + && $wgEnableEmail; |
| 95 | + return $data; |
| 96 | + } |
| 97 | + |
| 98 | + public function formSubmitCallback( $data ){ |
| 99 | + if ( $data['mailpassword'] ) { |
| 100 | + return $this->showMailPage( $data ); |
| 101 | + } else { |
| 102 | + return $this->processLogin( $data ); |
116 | 103 | } |
117 | | - # When switching accounts, it sucks to get automatically logged out |
118 | | - $returnToTitle = Title::newFromText( $this->mReturnTo ); |
119 | | - if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) { |
120 | | - $this->mReturnTo = ''; |
121 | | - $this->mReturnToQuery = ''; |
122 | | - } |
123 | 104 | } |
124 | 105 | |
125 | 106 | /** |
— | — | @@ -126,40 +107,26 @@ |
127 | 108 | * @param $msg String a message key for a warning/error message |
128 | 109 | * that may have been generated on a previous iteration |
129 | 110 | */ |
130 | | - protected function mainLoginForm( $msg, $msgtype = 'error' ) { |
131 | | - global $wgUser, $wgOut, $wgEnableEmail; |
| 111 | + protected function getForm() { |
| 112 | + global $wgUser, $wgOut, $wgRequest, $wgEnableEmail; |
132 | 113 | global $wgCookiePrefix, $wgLoginLanguageSelector; |
133 | 114 | global $wgAuth, $wgCookieExpiration; |
134 | 115 | |
135 | 116 | # Preload the name field with something if we can |
136 | | - if ( '' == $this->mUsername ) { |
137 | | - if ( $wgUser->isLoggedIn() ) { |
138 | | - $this->mUsername = $wgUser->getName(); |
139 | | - } elseif( isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ) { |
140 | | - $this->mUsername = $_COOKIE[$wgCookiePrefix.'UserName']; |
141 | | - } |
| 117 | + if ( $wgUser->isLoggedIn() ) { |
| 118 | + $username = $wgUser->getName(); |
| 119 | + } elseif( isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ) { |
| 120 | + $username = $_COOKIE[$wgCookiePrefix.'UserName']; |
| 121 | + } else { |
| 122 | + $username = false; |
142 | 123 | } |
143 | | - if( $this->mUsername ){ |
144 | | - $this->mFormFields['Name']['default'] = $this->mUsername; |
| 124 | + if( $username ){ |
| 125 | + $this->mFormFields['Name']['default'] = $username; |
145 | 126 | $this->mFormFields['Password']['autofocus'] = '1'; |
146 | 127 | } else { |
147 | 128 | $this->mFormFields['Name']['autofocus'] = '1'; |
148 | 129 | } |
149 | 130 | |
150 | | - # Parse the error message if we got one |
151 | | - if( $msg ){ |
152 | | - if( $msgtype == 'error' ){ |
153 | | - $msg = wfMsgExt( 'loginerror', 'parseinline' ) . ' ' . $msg; |
154 | | - } |
155 | | - $msg = Html::rawElement( |
156 | | - 'div', |
157 | | - array( 'class' => $msgtype . 'box' ), |
158 | | - $msg |
159 | | - ); |
160 | | - } else { |
161 | | - $msg = ''; |
162 | | - } |
163 | | - |
164 | 131 | # Make sure the returnTo strings don't get lost if the |
165 | 132 | # user changes language, etc |
166 | 133 | $linkq = array(); |
— | — | @@ -170,8 +137,8 @@ |
171 | 138 | } |
172 | 139 | |
173 | 140 | # Pass any language selection on to the mode switch link |
174 | | - if( $wgLoginLanguageSelector && $this->mLanguage ) |
175 | | - $linkq['uselang'] = $this->mLanguage; |
| 141 | + if( $wgLoginLanguageSelector && $wgRequest->getText( 'uselang' ) ) |
| 142 | + $linkq['uselang'] = $wgRequest->getText( 'uselang' ); |
176 | 143 | |
177 | 144 | $skin = $wgUser->getSkin(); |
178 | 145 | $link = $skin->link( |
— | — | @@ -182,7 +149,7 @@ |
183 | 150 | |
184 | 151 | # Don't show a "create account" link if the user can't |
185 | 152 | $link = $wgUser->isAllowed( 'createaccount' ) && !$wgUser->isLoggedIn() |
186 | | - ? wfMsgWikiHtml( 'nologin', $link ) |
| 153 | + ? wfMsgExt( 'nologin', array('parseinline','replaceafter'), $link ) |
187 | 154 | : ''; |
188 | 155 | |
189 | 156 | # Prepare language selection links as needed |
— | — | @@ -195,7 +162,7 @@ |
196 | 163 | |
197 | 164 | # Give authentication and captcha plugins a chance to |
198 | 165 | # modify the form, by hook or by using $wgAuth |
199 | | - $wgAuth->modifyUITemplate( $this, 'login' ); |
| 166 | + //$wgAuth->modifyUITemplate( $this, 'login' ); |
200 | 167 | wfRunHooks( 'UserLoginForm', array( &$this ) ); |
201 | 168 | |
202 | 169 | # The most likely use of the hook is to enable domains; |
— | — | @@ -211,21 +178,30 @@ |
212 | 179 | if( !($wgCookieExpiration > 0) ){ |
213 | 180 | # Remove it altogether |
214 | 181 | unset( $this->mFormFields['Remember'] ); |
215 | | - } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){ |
| 182 | + } elseif( $wgUser->getOption( 'rememberpassword' ) ){ |
216 | 183 | # Or check it by default |
217 | 184 | # FIXME: this doesn't always work? |
218 | | - $this->mFormFields['Remember']['checked'] = '1'; |
| 185 | + $this->mFormFields['Remember']['default'] = '1'; |
219 | 186 | } |
220 | 187 | |
| 188 | + $this->mFormFields['Token'] = array( |
| 189 | + 'type' => 'hidden', |
| 190 | + 'default' => Token::get( 'login' ), |
| 191 | + ); |
| 192 | + |
221 | 193 | $form = new HTMLForm( $this->mFormFields, '' ); |
222 | 194 | $form->setTitle( $this->getTitle() ); |
223 | 195 | $form->setSubmitText( wfMsg( 'login' ) ); |
224 | 196 | $form->setSubmitId( 'wpLoginAttempt' ); |
225 | 197 | $form->suppressReset(); |
226 | 198 | $form->setWrapperLegend( wfMsg( 'userlogin' ) ); |
| 199 | + $form->setTokenAction( 'login' ); |
227 | 200 | |
228 | 201 | $form->addHiddenField( 'returnto', $this->mReturnTo ); |
229 | 202 | $form->addHiddenField( 'returntoquery', $this->mReturnToQuery ); |
| 203 | + if( $wgRequest->getText( 'uselang' ) ){ |
| 204 | + $form->addHiddenField( 'uselang', $wgRequest->getText( 'uselang' ) ); |
| 205 | + } |
230 | 206 | |
231 | 207 | $form->addHeaderText( '' |
232 | 208 | . Html::rawElement( 'p', array( 'id' => 'userloginlink' ), |
— | — | @@ -236,7 +212,6 @@ |
237 | 213 | . $langSelector |
238 | 214 | ); |
239 | 215 | $form->addPreText( '' |
240 | | - . $msg |
241 | 216 | . Html::rawElement( |
242 | 217 | 'div', |
243 | 218 | array( 'id' => 'loginstart' ), |
— | — | @@ -252,7 +227,6 @@ |
253 | 228 | ); |
254 | 229 | |
255 | 230 | # Add a 'mail reset' button if available |
256 | | - $buttons = ''; |
257 | 231 | if( $wgEnableEmail && $wgAuth->allowPasswordChange() ){ |
258 | 232 | $form->addButton( |
259 | 233 | 'wpMailmypassword', |
— | — | @@ -261,61 +235,14 @@ |
262 | 236 | ); |
263 | 237 | } |
264 | 238 | |
| 239 | + $form->setFilterCallback( array( $this, 'formFilterCallback' ) ); |
| 240 | + $form->setSubmitCallback( array( $this, 'formSubmitCallback' ) ); |
265 | 241 | $form->loadData(); |
266 | | - |
267 | | - $wgOut->setPageTitle( wfMsg( 'login' ) ); |
268 | | - $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
269 | | - $wgOut->setArticleRelated( false ); |
270 | | - $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords |
271 | | - |
272 | | - $form->displayForm( '' ); |
273 | | - } |
274 | | - |
275 | | - /** |
276 | | - * Check if a session cookie is present. |
277 | | - * |
278 | | - * This will not pick up a cookie set during _this_ request, but is meant |
279 | | - * to ensure that the client is returning the cookie which was set on a |
280 | | - * previous pass through the system. |
281 | | - * |
282 | | - * @private |
283 | | - */ |
284 | | - protected function hasSessionCookie() { |
285 | | - global $wgDisableCookieCheck, $wgRequest; |
286 | | - return $wgDisableCookieCheck || $wgRequest->checkSessionCookie(); |
| 242 | + |
| 243 | + return $form; |
287 | 244 | } |
288 | 245 | |
289 | 246 | /** |
290 | | - * Do a redirect back to the same page, so we can check any |
291 | | - * new session cookies. |
292 | | - */ |
293 | | - protected function cookieRedirectCheck() { |
294 | | - global $wgOut; |
295 | | - |
296 | | - $query = array( 'wpCookieCheck' => '1'); |
297 | | - if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo; |
298 | | - $check = $this->getTitle()->getFullURL( $query ); |
299 | | - |
300 | | - return $wgOut->redirect( $check ); |
301 | | - } |
302 | | - |
303 | | - /** |
304 | | - * Check the cookies and show errors if they're not enabled. |
305 | | - * @param $type String action being performed |
306 | | - */ |
307 | | - protected function onCookieRedirectCheck() { |
308 | | - if ( $this->hasSessionCookie() ) { |
309 | | - return self::successfulLogin( |
310 | | - 'loginsuccess', |
311 | | - $this->mReturnTo, |
312 | | - $this->mReturnToQuery |
313 | | - ); |
314 | | - } else { |
315 | | - return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) ); |
316 | | - } |
317 | | - } |
318 | | - |
319 | | - /** |
320 | 247 | * Produce a bar of links which allow the user to select another language |
321 | 248 | * during login/registration but retain "returnto" |
322 | 249 | * @param $title Title to use in the link |
— | — | @@ -333,11 +260,13 @@ |
334 | 261 | $lang = trim( $lang, '* ' ); |
335 | 262 | $parts = explode( '|', $lang ); |
336 | 263 | if (count($parts) >= 2) { |
337 | | - $links[] = SpecialUserLogin::makeLanguageSelectorLink( |
| 264 | + $links[] = self::makeLanguageSelectorLink( |
338 | 265 | $parts[0], $parts[1], $title, $returnTo ); |
339 | 266 | } |
340 | 267 | } |
341 | | - return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) : ''; |
| 268 | + return count( $links ) > 0 |
| 269 | + ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) |
| 270 | + : ''; |
342 | 271 | } else { |
343 | 272 | return ''; |
344 | 273 | } |
— | — | @@ -419,29 +348,67 @@ |
420 | 349 | } |
421 | 350 | } |
422 | 351 | |
| 352 | + /** |
| 353 | + * Try and login with the data provided, and react appropriately. |
| 354 | + * @param $data Array from HTMLForm |
| 355 | + * @return Mixed Bool true, or String error |
| 356 | + */ |
| 357 | + protected function processLogin( $data ){ |
| 358 | + global $wgUser; |
| 359 | + |
| 360 | + $login = new Login( $data ); |
| 361 | + $result = $login->attemptLogin(); |
423 | 362 | |
424 | | - protected function processLogin(){ |
425 | | - global $wgUser, $wgAuth; |
426 | | - $result = $this->mLogin->attemptLogin(); |
427 | 363 | switch ( $result ) { |
428 | 364 | case Login::SUCCESS: |
429 | | - if( $this->hasSessionCookie() || $this->mSkipCookieCheck ) { |
430 | | - # Replace the language object to provide user interface in |
431 | | - # correct language immediately on this first page load. |
432 | | - global $wgLang, $wgRequest; |
433 | | - $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) ); |
434 | | - $wgLang = Language::factory( $code ); |
435 | | - return self::successfulLogin( |
436 | | - 'loginsuccess', |
437 | | - $this->mReturnTo, |
438 | | - $this->mReturnToQuery, |
439 | | - $this->mLogin->mLoginResult ); |
440 | | - } else { |
441 | | - # Do a redirect check to ensure that the cookies are |
442 | | - # being retained by the user's browser. |
443 | | - return $this->cookieRedirectCheck(); |
444 | | - } |
445 | | - break; |
| 365 | + Token::clear( 'login' ); |
| 366 | + # Replace the language object to provide user interface in |
| 367 | + # correct language immediately on this first page load. Note |
| 368 | + # that this only has any effect if we display a login splash |
| 369 | + # screen. |
| 370 | + global $wgLang, $wgRequest, $wgOut; |
| 371 | + $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) ); |
| 372 | + $wgLang = Language::factory( $code ); |
| 373 | + $wgOut->addHTML( self::successfulLogin( |
| 374 | + 'loginsuccess', |
| 375 | + $this->mReturnTo, |
| 376 | + $this->mReturnToQuery, |
| 377 | + $login->mLoginResult |
| 378 | + ) ); |
| 379 | + return true; |
| 380 | + |
| 381 | + case Login::RESET_PASS: |
| 382 | + Token::clear( 'login' ); |
| 383 | + # 'Shell out' to Special:ResetPass to get the user to |
| 384 | + # set a new permanent password from a temporary one. |
| 385 | + $reset = new SpecialResetpass(); |
| 386 | + $msg = wfMsgExt( 'resetpass_announce', 'parseinline' ); |
| 387 | + $reset->getForm(true)->displayForm( $msg ); |
| 388 | + return true; |
| 389 | + |
| 390 | + case Login::CREATE_BLOCKED: |
| 391 | + # Be nice about this, it's likely that this feature will be used |
| 392 | + # for blocking large numbers of innocent people, e.g. range blocks on |
| 393 | + # schools. Don't blame it on the user. There's a small chance that it |
| 394 | + # really is the user's fault, i.e. the username is blocked and they |
| 395 | + # haven't bothered to log out before trying to create an account to |
| 396 | + # evade it, but we'll leave that to their guilty conscience to figure |
| 397 | + # out. |
| 398 | + global $wgOut, $wgUser; |
| 399 | + $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) ); |
| 400 | + $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
| 401 | + $wgOut->setArticleRelated( false ); |
| 402 | + |
| 403 | + $ip = wfGetIP(); |
| 404 | + $blocker = User::whoIs( $wgUser->mBlock->mBy ); |
| 405 | + $blockReason = $wgUser->mBlock->mReason; |
| 406 | + |
| 407 | + if ( strval( $blockReason ) === '' ) { |
| 408 | + $blockReason = wfMsg( 'blockednoreason' ); |
| 409 | + } |
| 410 | + $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $blockReason, $blocker ); |
| 411 | + $wgOut->returnToMain( false ); |
| 412 | + return true; |
446 | 413 | |
447 | 414 | case Login::NO_NAME: |
448 | 415 | case Login::ILLEGAL: |
— | — | @@ -449,34 +416,23 @@ |
450 | 417 | case Login::WRONG_PASS: |
451 | 418 | case Login::EMPTY_PASS: |
452 | 419 | case Login::THROTTLED: |
453 | | - $this->mainLoginForm( wfMsgExt( $this->mLogin->mLoginResult, 'parseinline' ) ); |
454 | | - break; |
| 420 | + return wfMsgExt( $login->mLoginResult, 'parseinline' ); |
455 | 421 | |
456 | 422 | case Login::NOT_EXISTS: |
457 | 423 | if( $wgUser->isAllowed( 'createaccount' ) ){ |
458 | | - $this->mainLoginForm( wfMsgExt( 'nosuchuser', 'parseinline', htmlspecialchars( $this->mUsername ) ) ); |
| 424 | + return wfMsgExt( 'nosuchuser', 'parseinline', htmlspecialchars( $data['Name'] ) ); |
459 | 425 | } else { |
460 | | - $this->mainLoginForm( wfMsgExt( 'nosuchusershort', 'parseinline', htmlspecialchars( $this->mUsername ) ) ); |
| 426 | + return wfMsgExt( 'nosuchusershort', 'parseinline', htmlspecialchars( $data['Name'] ) ); |
461 | 427 | } |
462 | | - break; |
463 | 428 | |
464 | | - case Login::RESET_PASS: |
465 | | - # 'Shell out' to Special:ResetPass to get the user to |
466 | | - # set a new permanent password from a temporary one. |
467 | | - $reset = new SpecialResetpass(); |
468 | | - $reset->mHeaderMsg = wfMsgExt( 'resetpass_announce', 'parseinline' ); |
469 | | - $reset->mHeaderMsgType = 'success'; |
470 | | - $reset->execute( null ); |
471 | | - break; |
| 429 | + case Login::USER_BLOCKED: |
| 430 | + return wfMsgExt( 'login-userblocked', 'parseinline', $login->getUser()->getName() ); |
472 | 431 | |
473 | | - case Login::CREATE_BLOCKED: |
474 | | - $this->userBlockedMessage(); |
475 | | - break; |
476 | | - |
477 | 432 | case Login::ABORTED: |
478 | | - $msg = $this->mLogin->mLoginResult ? $this->mLogin->mLoginResult : $this->mLogin->mCreateResult; |
479 | | - $this->mainLoginForm( wfMsgExt( $msg, 'parseinline' ) ); |
480 | | - break; |
| 433 | + $msg = $login->mLoginResult |
| 434 | + ? $login->mLoginResult |
| 435 | + : $login->mCreateResult; |
| 436 | + return wfMsgExt( $msg, 'parseinline' ); |
481 | 437 | |
482 | 438 | default: |
483 | 439 | throw new MWException( "Unhandled case value: $result" ); |
— | — | @@ -484,54 +440,67 @@ |
485 | 441 | } |
486 | 442 | |
487 | 443 | /** |
488 | | - * Attempt to send the user a password-reset mail, and display |
| 444 | + * Attempt to send the user a password-reset mail, and return |
489 | 445 | * the results (good, bad or ugly). |
| 446 | + * @param $data Array from HTMLForm |
| 447 | + * @return Mixed Bool true on success, String HTML on failure |
490 | 448 | */ |
491 | | - protected function showMailPage(){ |
| 449 | + protected function showMailPage( $data ){ |
492 | 450 | global $wgOut; |
493 | | - $result = $this->mLogin->mailPassword(); |
| 451 | + $login = new Login( $data ); |
| 452 | + $result = $login->mailPassword(); |
494 | 453 | |
495 | 454 | switch( $result ){ |
| 455 | + |
| 456 | + case Login::SUCCESS: |
| 457 | + Token::clear( 'login' ); |
| 458 | + $wgOut->addWikiMsg( 'passwordsent', $login->getUser()->getName() ); |
| 459 | + return true; |
| 460 | + |
496 | 461 | case Login::READ_ONLY : |
497 | 462 | $wgOut->readOnlyPage(); |
498 | | - return; |
499 | | - case Login::MAIL_PASSCHANGE_FORBIDDEN: |
500 | | - $this->mainLoginForm( wfMsgExt( 'resetpass_forbidden', 'parseinline' ) ); |
501 | | - return; |
502 | | - case Login::MAIL_BLOCKED: |
503 | | - $this->mainLoginForm( wfMsgExt( 'blocked-mailpassword', 'parseinline' ) ); |
504 | | - return; |
| 463 | + return true; |
| 464 | + |
505 | 465 | case Login::MAIL_PING_THROTTLED: |
506 | 466 | $wgOut->rateLimited(); |
507 | | - return; |
| 467 | + return true; |
| 468 | + |
| 469 | + case Login::MAIL_PASSCHANGE_FORBIDDEN: |
| 470 | + return wfMsgExt( 'resetpass_forbidden', 'parseinline' ); |
| 471 | + |
| 472 | + case Login::MAIL_BLOCKED: |
| 473 | + return wfMsgExt( 'blocked-mailpassword', 'parseinline' ); |
| 474 | + |
| 475 | + case Login::USER_BLOCKED: |
| 476 | + return wfMsgExt( 'login-userblocked-reset', 'parseinline' ); |
| 477 | + |
508 | 478 | case Login::MAIL_PASS_THROTTLED: |
509 | 479 | global $wgPasswordReminderResendTime; |
510 | 480 | # Round the time in hours to 3 d.p., in case someone |
511 | 481 | # is specifying minutes or seconds. |
512 | | - $this->mainLoginForm( wfMsgExt( |
| 482 | + return wfMsgExt( |
513 | 483 | 'throttled-mailpassword', |
514 | 484 | array( 'parsemag' ), |
515 | 485 | round( $wgPasswordReminderResendTime, 3 ) |
516 | | - ) ); |
517 | | - return; |
| 486 | + ); |
| 487 | + |
518 | 488 | case Login::NO_NAME: |
519 | | - $this->mainLoginForm( wfMsgExt( 'noname', 'parseinline' ) ); |
520 | | - return; |
| 489 | + return wfMsgExt( 'noname', 'parseinline' ); |
| 490 | + |
521 | 491 | case Login::NOT_EXISTS: |
522 | | - $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mLogin->getUser()->getName() ) ) ); |
523 | | - return; |
| 492 | + return wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $login->getUser()->getName() ) ); |
| 493 | + |
524 | 494 | case Login::MAIL_EMPTY_EMAIL: |
525 | | - $this->mainLoginForm( wfMsgExt( 'noemail', 'parseinline', $this->mLogin->getUser()->getName() ) ); |
526 | | - return; |
| 495 | + return wfMsgExt( 'noemail', 'parseinline', $login->getUser()->getName() ); |
| 496 | + |
527 | 497 | case Login::MAIL_BAD_IP: |
528 | | - $this->mainLoginForm( wfMsgExt( 'badipaddress', 'parseinline' ) ); |
529 | | - return; |
| 498 | + return wfMsgExt( 'badipaddress', 'parseinline' ); |
| 499 | + |
530 | 500 | case Login::MAIL_ERROR: |
531 | | - $this->mainLoginForm( wfMsgExt( 'mailerror', 'parseinline', $this->mLogin->mMailResult->getMessage() ) ); |
532 | | - return; |
533 | | - case Login::SUCCESS: |
534 | | - $this->mainLoginForm( wfMsgExt( 'passwordsent', 'parseinline', $this->mLogin->getUser()->getName() ), 'success' ); |
535 | | - return; |
| 501 | + return wfMsgExt( 'mailerror', 'parseinline', $login->mMailResult->getMessage() ); |
| 502 | + |
| 503 | + default: |
| 504 | + throw new MWException( "Unhandled case value: $result" ); |
536 | 505 | } |
537 | 506 | } |
538 | 507 | |
Index: branches/happy-melon/phase3/includes/specials/SpecialCreateAccount.php |
— | — | @@ -6,16 +6,12 @@ |
7 | 7 | class SpecialCreateAccount extends SpecialPage { |
8 | 8 | |
9 | 9 | var $mUsername, $mPassword, $mRetype, $mReturnTo, $mPosted; |
10 | | - var $mCreateaccountMail, $mRemember, $mEmail, $mDomain, $mLanguage; |
| 10 | + var $mCreateaccountMail, $mEmail, $mDomain, $mLanguage; |
11 | 11 | var $mReturnToQuery; |
12 | | - |
13 | | - protected $mLogin; |
14 | 12 | |
15 | 13 | public $mDomains = array(); |
16 | 14 | |
17 | 15 | public $mUseEmail = true; # Can be switched off by AuthPlugins etc |
18 | | - public $mUseRealname = true; |
19 | | - public $mUseRemember = true; |
20 | 16 | |
21 | 17 | public $mFormHeader = ''; |
22 | 18 | public $mFormFields = array( |
— | — | @@ -52,12 +48,10 @@ |
53 | 49 | 'type' => 'text', |
54 | 50 | 'label-message' => 'yourrealname', |
55 | 51 | 'id' => 'wpRealName', |
56 | | - 'tabindex' => '1', |
57 | 52 | 'size' => '20', |
58 | 53 | ), |
59 | 54 | 'Remember' => array( |
60 | 55 | 'type' => 'check', |
61 | | - 'label-message' => 'remembermypassword', |
62 | 56 | 'id' => 'wpRemember', |
63 | 57 | ), |
64 | 58 | 'Domain' => array( |
— | — | @@ -71,16 +65,45 @@ |
72 | 66 | |
73 | 67 | public function __construct(){ |
74 | 68 | parent::__construct( 'CreateAccount', 'createaccount' ); |
75 | | - $this->mLogin = new Login(); |
| 69 | + |
76 | 70 | $this->mFormFields['RealName']['label-help'] = 'prefs-help-realname'; |
| 71 | + $this->mFormFields['Retype']['validation-callback'] = array( 'SpecialCreateAccount', 'formValidateRetype' ); |
| 72 | + |
| 73 | + |
| 74 | + global $wgCookieExpiration, $wgLang; |
| 75 | + $this->mFormFields['Remember']['label'] = wfMsgExt( |
| 76 | + 'remembermypassword', |
| 77 | + 'parseinline', |
| 78 | + $wgLang->formatNum( ceil( $wgCookieExpiration / 86400 ) ) |
| 79 | + ); |
| 80 | + |
| 81 | + global $wgRequest, $wgRedirectOnLogin; |
| 82 | + $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' ) |
| 83 | + && $wgEnableEmail; |
| 84 | + |
| 85 | + $this->mReturnTo = $wgRequest->getVal( 'returnto' ); |
| 86 | + $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' ); |
| 87 | + $this->mLanguage = $wgRequest->getText( 'uselang' ); |
| 88 | + |
| 89 | + if ( $wgRedirectOnLogin ) { |
| 90 | + $this->mReturnTo = $wgRedirectOnLogin; |
| 91 | + $this->mReturnToQuery = ''; |
| 92 | + } |
| 93 | + |
| 94 | + # When switching accounts, it sucks to get automatically logged out |
| 95 | + $returnToTitle = Title::newFromText( $this->mReturnTo ); |
| 96 | + if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) { |
| 97 | + $this->mReturnTo = ''; |
| 98 | + $this->mReturnToQuery = ''; |
| 99 | + } |
77 | 100 | } |
78 | 101 | |
79 | 102 | public function execute( $par ){ |
80 | 103 | global $wgUser, $wgOut; |
81 | 104 | |
82 | 105 | $this->setHeaders(); |
83 | | - $this->loadQuery(); |
84 | | - |
| 106 | + $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords |
| 107 | + |
85 | 108 | # Block signup here if in readonly. Keeps user from |
86 | 109 | # going through the process (filling out data, etc) |
87 | 110 | # and being informed later. |
— | — | @@ -98,78 +121,50 @@ |
99 | 122 | } elseif ( count( $permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) { |
100 | 123 | $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' ); |
101 | 124 | return; |
102 | | - } |
103 | | - |
104 | | - if( $this->mPosted ) { |
105 | | - $this->addNewAccount( $this->mCreateaccountMail ); |
106 | | - } else { |
107 | | - $this->showMainForm(''); |
108 | 125 | } |
| 126 | + |
| 127 | + $form = $this->getForm(); |
| 128 | + $form->show(); |
109 | 129 | } |
110 | 130 | |
111 | 131 | /** |
112 | | - * Load the member variables from the request parameters |
| 132 | + * Check that the user actually managed to type the password |
| 133 | + * in the same both times |
| 134 | + * @param unknown_type $data |
| 135 | + * @param unknown_type $alldata |
113 | 136 | */ |
114 | | - protected function loadQuery(){ |
115 | | - global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin; |
116 | | - $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' ) |
117 | | - && $wgEnableEmail; |
118 | | - |
119 | | - $this->mUsername = $wgRequest->getText( 'wpName' ); |
120 | | - $this->mPassword = $wgRequest->getText( 'wpPassword' ); |
121 | | - $this->mRetype = $wgRequest->getText( 'wpRetype' ); |
122 | | - $this->mDomain = $wgRequest->getText( 'wpDomain' ); |
123 | | - $this->mReturnTo = $wgRequest->getVal( 'returnto' ); |
124 | | - $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' ); |
125 | | - $this->mPosted = $wgRequest->wasPosted(); |
126 | | - $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' ) |
127 | | - && $wgEnableEmail; |
128 | | - $this->mRemember = $wgRequest->getCheck( 'wpRemember' ); |
129 | | - $this->mLanguage = $wgRequest->getText( 'uselang' ); |
130 | | - |
131 | | - if ( $wgRedirectOnLogin ) { |
132 | | - $this->mReturnTo = $wgRedirectOnLogin; |
133 | | - $this->mReturnToQuery = ''; |
| 137 | + public static function formValidateRetype( $retype, $alldata ){ |
| 138 | + # blank == blank, but the 'this field is required' validation |
| 139 | + # will catch that. |
| 140 | + if( $retype === '' ){ |
| 141 | + return true; |
134 | 142 | } |
135 | 143 | |
136 | | - if( $wgEnableEmail ) { |
137 | | - $this->mEmail = $wgRequest->getText( 'wpEmail' ); |
| 144 | + # The other password field could be 'Password' (Special:CreateAccount) |
| 145 | + # or 'NewPassword' (Special:ResetPass). |
| 146 | + if( isset( $alldata['Password'] ) ){ |
| 147 | + $password = $alldata['Password']; |
| 148 | + } elseif ( isset( $alldata['NewPassword'] ) ){ |
| 149 | + $password = $alldata['NewPassword']; |
138 | 150 | } else { |
139 | | - $this->mEmail = ''; |
| 151 | + $password = null; |
140 | 152 | } |
141 | | - if( !in_array( 'realname', $wgHiddenPrefs ) ) { |
142 | | - $this->mRealName = $wgRequest->getText( 'wpRealName' ); |
143 | | - } else { |
144 | | - $this->mRealName = ''; |
145 | | - } |
146 | | - |
147 | | - if( !$wgAuth->validDomain( $this->mDomain ) ) { |
148 | | - $this->mDomain = 'invaliddomain'; |
149 | | - } |
150 | | - $wgAuth->setDomain( $this->mDomain ); |
151 | | - |
152 | | - # When switching accounts, it sucks to get automatically logged out |
153 | | - $returnToTitle = Title::newFromText( $this->mReturnTo ); |
154 | | - if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) { |
155 | | - $this->mReturnTo = ''; |
156 | | - $this->mReturnToQuery = ''; |
157 | | - } |
| 153 | + |
| 154 | + return $retype === $password |
| 155 | + ? true |
| 156 | + : wfMsgExt( 'badretype', 'parseinline' ); |
158 | 157 | } |
159 | 158 | |
160 | 159 | /** |
161 | 160 | * Create a new user account from the provided data |
162 | 161 | */ |
163 | | - protected function addNewAccount( $byEmail=false ) { |
| 162 | + public function formSubmitCallback( $data ) { |
164 | 163 | global $wgUser, $wgEmailAuthentication; |
165 | | - |
166 | | - # Do a quick check that the user actually managed to type |
167 | | - # the password in the same both times |
168 | | - if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) { |
169 | | - return $this->showMainForm( wfMsgExt( 'badretype', 'parseinline' ) ); |
170 | | - } |
171 | 164 | |
172 | 165 | # Create the account and abort if there's a problem doing so |
173 | | - $status = $this->mLogin->attemptCreation( $byEmail ); |
| 166 | + $login = new Login( $data ); |
| 167 | + $status = $login->attemptCreation( $this->mCreateaccountMail ); |
| 168 | + |
174 | 169 | switch( $status ){ |
175 | 170 | case Login::SUCCESS: |
176 | 171 | case Login::MAIL_ERROR: |
— | — | @@ -183,52 +178,57 @@ |
184 | 179 | case Login::CREATE_BADNAME: |
185 | 180 | case Login::WRONG_PLUGIN_PASS: |
186 | 181 | case Login::ABORTED: |
187 | | - return $this->showMainForm( wfMsgExt( $this->mLogin->mCreateResult, 'parseinline' ) ); |
| 182 | + return wfMsgExt( $login->mCreateResult, 'parseinline' ); |
188 | 183 | |
189 | 184 | case Login::CREATE_SORBS: |
190 | | - return $this->showMainForm( wfMsgExt( 'sorbs_create_account_reason', 'parseinline' ) . ' (' . wfGetIP() . ')' ); |
| 185 | + return wfMsgExt( 'sorbs_create_account_reason', 'parseinline' ) . ' (' . wfGetIP() . ')'; |
191 | 186 | |
192 | 187 | case Login::CREATE_BLOCKED: |
193 | | - return $this->userBlockedMessage(); |
| 188 | + $this->userBlockedMessage(); |
| 189 | + return true; |
194 | 190 | |
195 | 191 | case Login::CREATE_BADPASS: |
196 | 192 | global $wgMinimalPasswordLength; |
197 | | - return $this->showMainForm( wfMsgExt( $this->mLogin->mCreateResult, array( 'parsemag' ), $wgMinimalPasswordLength ) ); |
| 193 | + return wfMsgExt( $login->mCreateResult, array( 'parsemag' ), $wgMinimalPasswordLength ); |
198 | 194 | |
199 | 195 | case Login::THROTTLED: |
200 | 196 | global $wgAccountCreationThrottle; |
201 | | - return $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) ); |
| 197 | + return wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ); |
202 | 198 | |
203 | 199 | default: |
204 | 200 | throw new MWException( "Unhandled status code $status in " . __METHOD__ ); |
205 | 201 | } |
| 202 | + |
| 203 | + Token::clear( 'createaccount' ); |
206 | 204 | |
207 | 205 | # If we showed up language selection links, and one was in use, be |
208 | 206 | # smart (and sensible) and save that language as the user's preference |
209 | 207 | global $wgLoginLanguageSelector; |
210 | | - if( $wgLoginLanguageSelector && $this->mLanguage ) |
211 | | - $this->mLogin->getUser()->setOption( 'language', $this->mLanguage ); |
212 | | - $this->mLogin->getUser()->saveSettings(); |
| 208 | + if( $wgLoginLanguageSelector && $this->mLanguage ){ |
| 209 | + $login->getUser()->setOption( 'language', $this->mLanguage ); |
| 210 | + $login->getUser()->saveSettings(); |
| 211 | + } |
213 | 212 | |
214 | | - if( $byEmail ) { |
| 213 | + if( $this->mCreateaccountMail ) { |
215 | 214 | if( $status == Login::MAIL_ERROR ){ |
216 | 215 | # FIXME: we are totally screwed if we end up here... |
217 | | - $this->showMainForm( wfMsgExt( 'mailerror', 'parseinline', $this->mLogin->mMailResult->getMessage() ) ); |
| 216 | + return wfMsgExt( 'mailerror', 'parseinline', $login->mMailResult->getMessage() ); |
218 | 217 | } else { |
219 | 218 | global $wgOut; |
220 | 219 | $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) ); |
221 | | - $wgOut->addWikiMsg( 'accmailtext', $this->mLogin->getUser()->getName(), $this->mLogin->getUser()->getEmail() ); |
| 220 | + $wgOut->addWikiMsg( 'accmailtext', $login->getUser()->getName(), $login->getUser()->getEmail() ); |
222 | 221 | $wgOut->returnToMain( false ); |
| 222 | + return true; |
223 | 223 | } |
224 | 224 | |
225 | 225 | } else { |
226 | 226 | |
227 | 227 | # There might be a message stored from the confirmation mail |
228 | 228 | # send, which we can display. |
229 | | - if( $wgEmailAuthentication && $this->mLogin->mMailResult ) { |
| 229 | + if( $wgEmailAuthentication && $login->mMailResult ) { |
230 | 230 | global $wgOut; |
231 | | - if( WikiError::isError( $this->mLogin->mMailResult ) ) { |
232 | | - $wgOut->addWikiMsg( 'confirmemail_sendfailed', $this->mLogin->mMailResult->getMessage() ); |
| 231 | + if( WikiError::isError( $login->mMailResult ) ) { |
| 232 | + $wgOut->addWikiMsg( 'confirmemail_sendfailed', $login->mMailResult->getMessage() ); |
233 | 233 | } else { |
234 | 234 | $wgOut->addWikiMsg( 'confirmemail_oncreate' ); |
235 | 235 | } |
— | — | @@ -238,19 +238,15 @@ |
239 | 239 | # one and set session cookies then show a "welcome" message |
240 | 240 | # or a "need cookies" message as needed |
241 | 241 | if( $wgUser->isAnon() ) { |
242 | | - $wgUser = $this->mLogin->getUser(); |
243 | | - $wgUser->setCookies(); |
244 | | - if( $this->hasSessionCookie() ) { |
245 | | - return $this->successfulCreation(); |
246 | | - } else { |
247 | | - return $this->cookieRedirectCheck(); |
248 | | - } |
| 242 | + $login->attemptLogin( true ); |
| 243 | + $this->successfulCreation(); |
| 244 | + return true; |
249 | 245 | } else { |
250 | 246 | # Show confirmation that the account was created |
251 | 247 | global $wgOut; |
252 | 248 | $self = SpecialPage::getTitleFor( 'Userlogin' ); |
253 | 249 | $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) ); |
254 | | - $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $this->mLogin->getUser()->getName() ) ); |
| 250 | + $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $login->getUser()->getName() ) ); |
255 | 251 | $wgOut->returnToMain( false, $self ); |
256 | 252 | return true; |
257 | 253 | } |
— | — | @@ -309,24 +305,10 @@ |
310 | 306 | * @param $msg String HTML of message received previously |
311 | 307 | * @param $msgtype String type of message, usually 'error' |
312 | 308 | */ |
313 | | - protected function showMainForm( $msg, $msgtype = 'error' ) { |
| 309 | + protected function getForm() { |
314 | 310 | global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail; |
315 | 311 | global $wgCookiePrefix, $wgLoginLanguageSelector; |
316 | 312 | global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration; |
317 | | - |
318 | | - # Parse the error message if we got one |
319 | | - if( $msg ){ |
320 | | - if( $msgtype == 'error' ){ |
321 | | - $msg = wfMsgExt( 'createaccounterror', array( 'parseinline', 'replaceafter' ), $msg ); |
322 | | - } |
323 | | - $msg = Html::rawElement( |
324 | | - 'div', |
325 | | - array( 'class' => $msgtype . 'box' ), |
326 | | - $msg |
327 | | - ); |
328 | | - } else { |
329 | | - $msg = ''; |
330 | | - } |
331 | 313 | |
332 | 314 | # Make sure the returnTo strings don't get lost if the |
333 | 315 | # user changes language, etc |
— | — | @@ -338,8 +320,9 @@ |
339 | 321 | } |
340 | 322 | |
341 | 323 | # Pass any language selection on to the mode switch link |
342 | | - if( $wgLoginLanguageSelector && $this->mLanguage ) |
| 324 | + if( $wgLoginLanguageSelector && $this->mLanguage ){ |
343 | 325 | $linkq['uselang'] = $this->mLanguage; |
| 326 | + } |
344 | 327 | |
345 | 328 | $skin = $wgUser->getSkin(); |
346 | 329 | $link = $skin->link( |
— | — | @@ -361,7 +344,7 @@ |
362 | 345 | |
363 | 346 | # Give authentication and captcha plugins a chance to |
364 | 347 | # modify the form, by hook or by using $wgAuth |
365 | | - $wgAuth->modifyUITemplate( $this, 'new' ); |
| 348 | + //$wgAuth->modifyUITemplate( $this, 'new' ); |
366 | 349 | wfRunHooks( 'UserCreateForm', array( &$this ) ); |
367 | 350 | |
368 | 351 | # The most likely use of the hook is to enable domains; |
— | — | @@ -386,18 +369,18 @@ |
387 | 370 | } |
388 | 371 | |
389 | 372 | # Or to play with realname |
390 | | - if( in_array( 'realname', $wgHiddenPrefs ) || !$this->mUseRealname ){ |
| 373 | + if( in_array( 'realname', $wgHiddenPrefs ) ){ |
391 | 374 | unset( $this->mFormFields['Realname'] ); |
392 | 375 | } |
393 | 376 | |
394 | 377 | # Or to tweak the 'remember my password' checkbox |
395 | | - if( !($wgCookieExpiration > 0) || !$this->mUseRemember ){ |
| 378 | + if( !($wgCookieExpiration > 0) ){ |
396 | 379 | # Remove it altogether |
397 | 380 | unset( $this->mFormFields['Remember'] ); |
398 | | - } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){ |
| 381 | + } elseif( $wgUser->getOption( 'rememberpassword' ) ){ |
399 | 382 | # Or check it by default |
400 | 383 | # FIXME: this doesn't always work? |
401 | | - $this->mFormFields['Remember']['checked'] = '1'; |
| 384 | + $this->mFormFields['Remember']['default'] = '1'; |
402 | 385 | } |
403 | 386 | |
404 | 387 | $form = new HTMLForm( $this->mFormFields ); |
— | — | @@ -407,9 +390,13 @@ |
408 | 391 | $form->setSubmitId( 'wpCreateaccount' ); |
409 | 392 | $form->suppressReset(); |
410 | 393 | $form->setWrapperLegend( wfMsg( 'createaccount' ) ); |
| 394 | + $form->setTokenAction( 'createaccount' ); |
411 | 395 | |
412 | 396 | $form->addHiddenField( 'returnto', $this->mReturnTo ); |
413 | 397 | $form->addHiddenField( 'returntoquery', $this->mReturnToQuery ); |
| 398 | + if( $this->mLanguage ){ |
| 399 | + $form->addHiddenField( 'uselang', $this->mLanguage ); |
| 400 | + } |
414 | 401 | |
415 | 402 | $form->addHeaderText( '' |
416 | 403 | . Html::rawElement( 'p', array( 'id' => 'userloginlink' ), |
— | — | @@ -418,7 +405,6 @@ |
419 | 406 | . $langSelector |
420 | 407 | ); |
421 | 408 | $form->addPreText( '' |
422 | | - . $msg |
423 | 409 | . Html::rawElement( |
424 | 410 | 'div', |
425 | 411 | array( 'id' => 'signupstart' ), |
— | — | @@ -442,61 +428,12 @@ |
443 | 429 | ); |
444 | 430 | } |
445 | 431 | |
| 432 | + $form->setSubmitCallback( array( $this, 'formSubmitCallback' ) ); |
446 | 433 | $form->loadData(); |
447 | | - |
448 | | - $wgOut->setPageTitle( wfMsg( 'createaccount' ) ); |
449 | | - $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
450 | | - $wgOut->setArticleRelated( false ); |
451 | | - $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords |
452 | | - |
453 | 434 | |
454 | | - $form->displayForm( false ); |
| 435 | + return $form; |
455 | 436 | |
456 | 437 | } |
457 | | - |
458 | | - /** |
459 | | - * Check if a session cookie is present. |
460 | | - * |
461 | | - * This will not pick up a cookie set during _this_ request, but is meant |
462 | | - * to ensure that the client is returning the cookie which was set on a |
463 | | - * previous pass through the system. |
464 | | - * |
465 | | - * @private |
466 | | - */ |
467 | | - protected function hasSessionCookie() { |
468 | | - global $wgDisableCookieCheck, $wgRequest; |
469 | | - return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie(); |
470 | | - } |
471 | | - |
472 | | - /** |
473 | | - * Do a redirect back to the same page, so we can check any |
474 | | - * new session cookies. |
475 | | - */ |
476 | | - protected function cookieRedirectCheck() { |
477 | | - global $wgOut; |
478 | | - |
479 | | - $query = array( 'wpCookieCheck' => '1' ); |
480 | | - if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo; |
481 | | - $check = $this->getTitle()->getFullURL( $query ); |
482 | | - |
483 | | - return $wgOut->redirect( $check ); |
484 | | - } |
485 | | - |
486 | | - /** |
487 | | - * Check the cookies and show errors if they're not enabled. |
488 | | - * @param $type String action being performed |
489 | | - */ |
490 | | - protected function onCookieRedirectCheck() { |
491 | | - if ( !$this->hasSessionCookie() ) { |
492 | | - return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) ); |
493 | | - } else { |
494 | | - return SpecialUserlogin::successfulLogin( |
495 | | - array( 'welcomecreate' ), |
496 | | - $this->mReturnTo, |
497 | | - $this->mReturnToQuery |
498 | | - ); |
499 | | - } |
500 | | - } |
501 | 438 | |
502 | 439 | /** |
503 | 440 | * Add text to the header. Only write to $mFormHeader directly |
Index: branches/happy-melon/phase3/includes/specials/SpecialResetpass.php |
— | — | @@ -1,5 +1,23 @@ |
2 | 2 | <?php |
3 | 3 | /** |
| 4 | + * |
| 5 | + * This program is free software; you can redistribute it and/or modify |
| 6 | + * it under the terms of the GNU General Public License as published by |
| 7 | + * the Free Software Foundation; either version 2 of the License, or |
| 8 | + * (at your option) any later version. |
| 9 | + * |
| 10 | + * This program is distributed in the hope that it will be useful, |
| 11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | + * GNU General Public License for more details. |
| 14 | + * |
| 15 | + * You should have received a copy of the GNU General Public License along |
| 16 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 17 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 18 | + * http://www.gnu.org/copyleft/gpl.html |
| 19 | + */ |
| 20 | + |
| 21 | +/** |
4 | 22 | * @file |
5 | 23 | * @ingroup SpecialPage |
6 | 24 | */ |
— | — | @@ -9,17 +27,19 @@ |
10 | 28 | * @ingroup SpecialPage |
11 | 29 | */ |
12 | 30 | class SpecialResetpass extends SpecialPage { |
13 | | - public function __construct() { |
14 | | - parent::__construct( 'Resetpass' ); |
15 | | - } |
16 | 31 | |
17 | 32 | public $mFormFields = array( |
18 | | - 'Name' => array( |
| 33 | + 'NameInfo' => array( |
19 | 34 | 'type' => 'info', |
20 | 35 | 'label-message' => 'yourname', |
21 | 36 | 'default' => '', |
22 | 37 | ), |
23 | | - 'Password' => array( |
| 38 | + 'Name' => array( |
| 39 | + 'type' => 'hidden', |
| 40 | + 'name' => 'wpName', |
| 41 | + 'default' => null, |
| 42 | + ), |
| 43 | + 'OldPassword' => array( |
24 | 44 | 'type' => 'password', |
25 | 45 | 'label-message' => 'oldpassword', |
26 | 46 | 'size' => '20', |
— | — | @@ -42,35 +62,44 @@ |
43 | 63 | ), |
44 | 64 | 'Remember' => array( |
45 | 65 | 'type' => 'check', |
46 | | - 'label-message' => 'remembermypassword', |
47 | 66 | 'id' => 'wpRemember', |
48 | 67 | ), |
49 | 68 | ); |
50 | | - public $mSubmitMsg = 'resetpass-submit-loggedin'; |
51 | | - public $mHeaderMsg = ''; |
52 | | - public $mHeaderMsgType = 'error'; |
53 | 69 | |
54 | | - public $mUsername; |
55 | | - public $mOldpass; |
56 | | - public $mNewpass; |
57 | | - public $mRetype; |
| 70 | + protected $mUsername; |
| 71 | + protected $mLogin; |
58 | 72 | |
| 73 | + public function __construct() { |
| 74 | + global $wgRequest, $wgUser, $wgLang, $wgCookieExpiration; |
| 75 | + |
| 76 | + parent::__construct( 'Resetpass' ); |
| 77 | + $this->mFormFields['Retype']['validation-callback'] = array( 'SpecialCreateAccount', 'formValidateRetype' ); |
| 78 | + |
| 79 | + $this->mUsername = $wgRequest->getVal( 'wpName', $wgUser->getName() ); |
| 80 | + $this->mReturnTo = $wgRequest->getVal( 'returnto' ); |
| 81 | + $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' ); |
| 82 | + |
| 83 | + $this->mFormFields['Remember']['label'] = wfMsgExt( |
| 84 | + 'remembermypassword', |
| 85 | + 'parseinline', |
| 86 | + $wgLang->formatNum( ceil( $wgCookieExpiration / 86400 ) ) |
| 87 | + ); |
| 88 | + } |
| 89 | + |
59 | 90 | /** |
60 | 91 | * Main execution point |
61 | 92 | */ |
62 | | - function execute( $par ) { |
| 93 | + public function execute( $par ) { |
63 | 94 | global $wgUser, $wgAuth, $wgOut, $wgRequest; |
64 | 95 | |
65 | | - $this->mUsername = $wgRequest->getVal( 'wpName', $wgUser->getName() ); |
66 | | - $this->mOldpass = $wgRequest->getVal( 'wpPassword' ); |
67 | | - $this->mNewpass = $wgRequest->getVal( 'wpNewPassword' ); |
68 | | - $this->mRetype = $wgRequest->getVal( 'wpRetype' ); |
69 | | - $this->mRemember = $wgRequest->getVal( 'wpRemember' ); |
70 | | - $this->mReturnTo = $wgRequest->getVal( 'returnto' ); |
71 | | - $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' ); |
| 96 | + if ( wfReadOnly() ) { |
| 97 | + $wgOut->readOnlyPage(); |
| 98 | + return; |
| 99 | + } |
72 | 100 | |
73 | 101 | $this->setHeaders(); |
74 | 102 | $this->outputHeader(); |
| 103 | + $wgOut->disallowUserJs(); |
75 | 104 | |
76 | 105 | if( wfReadOnly() ){ |
77 | 106 | $wgOut->readOnlyPage(); |
— | — | @@ -85,21 +114,22 @@ |
86 | 115 | $wgOut->showErrorPage( 'errorpagetitle', 'resetpass-no-info' ); |
87 | 116 | return false; |
88 | 117 | } |
| 118 | + |
| 119 | + $this->getForm()->show(); |
| 120 | + |
| 121 | + } |
| 122 | + |
| 123 | + public function formSubmitCallback( $data ){ |
| 124 | + $data['Password'] = $data['OldPassword']; |
| 125 | + $this->mLogin = new Login( $data ); |
| 126 | + $result = $this->attemptReset( $data ); |
89 | 127 | |
90 | | - $data = array( |
91 | | - 'wpName' => $this->mUsername, |
92 | | - 'wpPassword' => $this->mOldpass, |
93 | | - ); |
94 | | - $this->mLogin = new Login( new FauxRequest( $data, true ) ); |
95 | | - |
96 | | - if( $wgRequest->wasPosted() |
97 | | - && $wgUser->matchEditToken( $wgRequest->getVal('wpEditToken') ) |
98 | | - && $this->attemptReset() ) |
99 | | - { |
| 128 | + if( $result === true ){ |
100 | 129 | # Log the user in if they're not already (ie we're |
101 | 130 | # coming from the e-mail-password-reset route |
| 131 | + global $wgUser; |
102 | 132 | if( !$wgUser->isLoggedIn() ) { |
103 | | - $this->mLogin->attemptLogin( $this->mNewpass ); |
| 133 | + $this->mLogin->attemptLogin( $data['NewPassword'] ); |
104 | 134 | # Redirect out to the appropriate target. |
105 | 135 | SpecialUserlogin::successfulLogin( |
106 | 136 | 'resetpass_success', |
— | — | @@ -115,81 +145,82 @@ |
116 | 146 | $this->mReturnToQuery |
117 | 147 | ); |
118 | 148 | } |
| 149 | + return true; |
119 | 150 | } else { |
120 | | - $this->showForm(); |
| 151 | + return $result; |
121 | 152 | } |
122 | 153 | } |
123 | 154 | |
124 | | - function showForm() { |
125 | | - global $wgOut, $wgUser; |
126 | | - |
127 | | - $wgOut->disallowUserJs(); |
| 155 | + public function getForm( $reset=false ) { |
| 156 | + global $wgOut, $wgUser, $wgRequest; |
128 | 157 | |
129 | | - if( $wgUser->isLoggedIn() ){ |
130 | | - unset( $this->mFormFields['Remember'] ); |
131 | | - } else { |
| 158 | + if( $reset || $wgRequest->getCheck( 'reset' ) ){ |
132 | 159 | # Request is coming from Special:UserLogin after it |
133 | 160 | # authenticated someone with a temporary password. |
134 | | - $this->mFormFields['Password']['label-message'] = 'resetpass-temp-password'; |
135 | | - $this->mSubmitMsg = 'resetpass_submit'; |
| 161 | + $this->mFormFields['OldPassword']['label-message'] = 'resetpass-temp-password'; |
| 162 | + $submitMsg = 'resetpass_submit'; |
| 163 | + $this->mFormFields['OldPassword']['default'] = $wgRequest->getText( 'wpPassword' ); |
| 164 | + #perpetuate |
| 165 | + $this->mFormFields['reset'] = array( |
| 166 | + 'type' => 'hidden', |
| 167 | + 'default' => '1', |
| 168 | + ); |
| 169 | + } else { |
| 170 | + unset( $this->mFormFields['Remember'] ); |
| 171 | + $submitMsg = 'resetpass-submit-loggedin'; |
136 | 172 | } |
137 | | - $this->mFormFields['Name']['default'] = $this->mUsername; |
138 | 173 | |
139 | | - $header = $this->mHeaderMsg |
140 | | - ? Html::element( 'div', array( 'class' => "{$this->mHeaderMsgType}box" ), wfMsgExt( $this->mHeaderMsg, 'parse' ) ) |
141 | | - : ''; |
| 174 | + $this->mFormFields['Name']['default'] = |
| 175 | + $this->mFormFields['NameInfo']['default'] = $this->mUsername; |
142 | 176 | |
143 | 177 | $form = new HTMLForm( $this->mFormFields, '' ); |
144 | 178 | $form->suppressReset(); |
145 | | - $form->setSubmitText( wfMsg( $this->mSubmitMsg ) ); |
| 179 | + $form->setSubmitText( wfMsg( $submitMsg ) ); |
146 | 180 | $form->setTitle( $this->getTitle() ); |
147 | | - $form->addHiddenField( 'wpName', $this->mUsername ); |
148 | 181 | $form->addHiddenField( 'returnto', $this->mReturnTo ); |
149 | 182 | $form->setWrapperLegend( wfMsg( 'resetpass_header' ) ); |
| 183 | + |
| 184 | + $form->setSubmitCallback( array( $this, 'formSubmitCallback' ) ); |
150 | 185 | $form->loadData(); |
151 | 186 | |
152 | | - $form->displayForm( $this->mHeaderMsg ); |
| 187 | + return $form; |
153 | 188 | } |
154 | 189 | |
155 | 190 | /** |
156 | 191 | * Try to reset the user's password |
157 | 192 | */ |
158 | | - protected function attemptReset() { |
| 193 | + protected function attemptReset( $data ) { |
159 | 194 | |
160 | | - if( !$this->mUsername |
161 | | - || !$this->mNewpass |
162 | | - || !$this->mRetype ) |
| 195 | + if( !$data['Name'] |
| 196 | + || !$data['OldPassword'] |
| 197 | + || !$data['NewPassword'] |
| 198 | + || !$data['Retype'] ) |
163 | 199 | { |
164 | 200 | return false; |
165 | 201 | } |
166 | 202 | |
167 | 203 | $user = $this->mLogin->getUser(); |
168 | 204 | if( !( $user instanceof User ) ){ |
169 | | - $this->mHeaderMsg = wfMsgExt( 'nosuchuser', 'parse' ); |
170 | | - return false; |
| 205 | + return wfMsgExt( 'nosuchuser', 'parse' ); |
171 | 206 | } |
172 | 207 | |
173 | | - if( $this->mNewpass !== $this->mRetype ) { |
174 | | - wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'badretype' ) ); |
175 | | - $this->mHeaderMsg = wfMsgExt( 'badretype', 'parse' ); |
176 | | - return false; |
| 208 | + if( $data['NewPassword'] !== $data['Retype'] ) { |
| 209 | + wfRunHooks( 'PrefsPasswordAudit', array( $user, $data['NewPassword'], 'badretype' ) ); |
| 210 | + return wfMsgExt( 'badretype', 'parse' ); |
177 | 211 | } |
178 | 212 | |
179 | | - if( !$user->checkPassword( $this->mOldpass ) && !$user->checkTemporaryPassword( $this->mOldpass ) ) |
| 213 | + if( !$user->checkPassword( $data['OldPassword'] ) && !$user->checkTemporaryPassword( $data['OldPassword'] ) ) |
180 | 214 | { |
181 | | - wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'wrongpassword' ) ); |
182 | | - $this->mHeaderMsg = wfMsgExt( 'resetpass-wrong-oldpass', 'parse' ); |
183 | | - return false; |
| 215 | + wfRunHooks( 'PrefsPasswordAudit', array( $user, $data['NewPassword'], 'wrongpassword' ) ); |
| 216 | + return wfMsgExt( 'resetpass-wrong-oldpass', 'parse' ); |
184 | 217 | } |
185 | 218 | |
186 | 219 | try { |
187 | | - $user->setPassword( $this->mNewpass ); |
188 | | - wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'success' ) ); |
189 | | - $this->mNewpass = $this->mOldpass = $this->mRetypePass = ''; |
| 220 | + $user->setPassword( $data['NewPassword'] ); |
| 221 | + wfRunHooks( 'PrefsPasswordAudit', array( $user, $data['NewPassword'], 'success' ) ); |
190 | 222 | } catch( PasswordError $e ) { |
191 | | - wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'error' ) ); |
192 | | - $this->mHeaderMsg = $e->getMessage(); |
193 | | - return false; |
| 223 | + wfRunHooks( 'PrefsPasswordAudit', array( $user, $data['NewPassword'], 'error' ) ); |
| 224 | + return $e->getMessage(); |
194 | 225 | } |
195 | 226 | |
196 | 227 | $user->setCookies(); |