Merge "Speed up password-handling in the unit tests"
[lhc/web/wiklou.git] / includes / specials / SpecialUserLogin.php
1 <?php
2 /**
3 * Implements Special:UserLogin
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 * @file
21 * @ingroup SpecialPage
22 */
23
24 use MediaWiki\Auth\AuthManager;
25 use MediaWiki\Logger\LoggerFactory;
26 use Psr\Log\LogLevel;
27
28 /**
29 * Implements Special:UserLogin
30 *
31 * @ingroup SpecialPage
32 */
33 class SpecialUserLogin extends LoginSignupSpecialPage {
34 protected static $allowedActions = [
35 AuthManager::ACTION_LOGIN,
36 AuthManager::ACTION_LOGIN_CONTINUE
37 ];
38
39 protected static $messages = [
40 'authform-newtoken' => 'nocookiesforlogin',
41 'authform-notoken' => 'sessionfailure',
42 'authform-wrongtoken' => 'sessionfailure',
43 ];
44
45 public function __construct() {
46 parent::__construct( 'Userlogin' );
47 }
48
49 public function doesWrites() {
50 return true;
51 }
52
53 protected function getLoginSecurityLevel() {
54 return false;
55 }
56
57 protected function getDefaultAction( $subPage ) {
58 return AuthManager::ACTION_LOGIN;
59 }
60
61 public function getDescription() {
62 return $this->msg( 'login' )->text();
63 }
64
65 public function setHeaders() {
66 // override the page title if we are doing a forced reauthentication
67 parent::setHeaders();
68 if ( $this->securityLevel && $this->getUser()->isLoggedIn() ) {
69 $this->getOutput()->setPageTitle( $this->msg( 'login-security' ) );
70 }
71 }
72
73 protected function isSignup() {
74 return false;
75 }
76
77 protected function beforeExecute( $subPage ) {
78 if ( $subPage === 'signup' || $this->getRequest()->getText( 'type' ) === 'signup' ) {
79 // B/C for old account creation URLs
80 $title = SpecialPage::getTitleFor( 'CreateAccount' );
81 $query = array_diff_key( $this->getRequest()->getValues(),
82 array_fill_keys( [ 'type', 'title' ], true ) );
83 $url = $title->getFullURL( $query, false, PROTO_CURRENT );
84 $this->getOutput()->redirect( $url );
85 return false;
86 }
87 return parent::beforeExecute( $subPage );
88 }
89
90 /**
91 * Run any hooks registered for logins, then HTTP redirect to
92 * $this->mReturnTo (or Main Page if that's undefined). Formerly we had a
93 * nice message here, but that's really not as useful as just being sent to
94 * wherever you logged in from. It should be clear that the action was
95 * successful, given the lack of error messages plus the appearance of your
96 * name in the upper right.
97 * @param bool $direct True if the action was successful just now; false if that happened
98 * pre-redirection (so this handler was called already)
99 * @param StatusValue|null $extraMessages
100 */
101 protected function successfulAction( $direct = false, $extraMessages = null ) {
102 global $wgSecureLogin;
103
104 $user = $this->targetUser ?: $this->getUser();
105 $session = $this->getRequest()->getSession();
106
107 if ( $direct ) {
108 $user->touch();
109
110 $this->clearToken();
111
112 if ( $user->requiresHTTPS() ) {
113 $this->mStickHTTPS = true;
114 }
115 $session->setForceHTTPS( $wgSecureLogin && $this->mStickHTTPS );
116
117 // If the user does not have a session cookie at this point, they probably need to
118 // do something to their browser.
119 if ( !$this->hasSessionCookie() ) {
120 $this->mainLoginForm( [ /*?*/ ], $session->getProvider()->whyNoSession() );
121 // TODO something more specific? This used to use nocookieslogin
122 return;
123 }
124 }
125
126 # Run any hooks; display injected HTML if any, else redirect
127 $injected_html = '';
128 Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html ] );
129
130 if ( $injected_html !== '' || $extraMessages ) {
131 $this->showSuccessPage( 'success', $this->msg( 'loginsuccesstitle' ),
132 'loginsuccess', $injected_html, $extraMessages );
133 } else {
134 $helper = new LoginHelper( $this->getContext() );
135 $helper->showReturnToPage( 'successredirect', $this->mReturnTo, $this->mReturnToQuery,
136 $this->mStickHTTPS );
137 }
138 }
139
140 protected function getToken() {
141 return $this->getRequest()->getSession()->getToken( '', 'login' );
142 }
143
144 protected function clearToken() {
145 return $this->getRequest()->getSession()->resetToken( 'login' );
146 }
147
148 protected function getTokenName() {
149 return 'wpLoginToken';
150 }
151
152 protected function getGroupName() {
153 return 'login';
154 }
155
156 protected function logAuthResult( $success, $status = null ) {
157 LoggerFactory::getInstance( 'authmanager-stats' )->info( 'Login attempt', [
158 'event' => 'login',
159 'successful' => $success,
160 'status' => $status,
161 ] );
162 }
163 }