Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 66 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
LoginCompleteHookHandler | |
0.00% |
0 / 66 |
|
0.00% |
0 / 4 |
210 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
onUserLoginComplete | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
72 | |||
onTempUserCreatedRedirect | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
6 | |||
getRedirectUrl | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\Extension\CentralAuth\Hooks\Handlers; |
22 | |
23 | use MediaWiki\Config\Config; |
24 | use MediaWiki\Context\RequestContext; |
25 | use MediaWiki\Extension\CentralAuth\CentralAuthTokenManager; |
26 | use MediaWiki\Extension\CentralAuth\Config\CAMainConfigNames; |
27 | use MediaWiki\Extension\CentralAuth\Hooks\CentralAuthHookRunner; |
28 | use MediaWiki\Extension\CentralAuth\SharedDomainUtils; |
29 | use MediaWiki\Extension\CentralAuth\Special\SpecialCentralLogin; |
30 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
31 | use MediaWiki\Hook\TempUserCreatedRedirectHook; |
32 | use MediaWiki\Hook\UserLoginCompleteHook; |
33 | use MediaWiki\HookContainer\HookContainer; |
34 | use MediaWiki\Logger\LoggerFactory; |
35 | use MediaWiki\Session\Session; |
36 | use MediaWiki\Title\Title; |
37 | use MediaWiki\User\User; |
38 | use MediaWiki\User\UserIdentity; |
39 | use MediaWiki\WikiMap\WikiMap; |
40 | use MWCryptRand; |
41 | |
42 | class LoginCompleteHookHandler implements |
43 | UserLoginCompleteHook, |
44 | TempUserCreatedRedirectHook |
45 | { |
46 | |
47 | private Config $config; |
48 | private CentralAuthTokenManager $tokenManager; |
49 | private CentralAuthHookRunner $caHookRunner; |
50 | private SharedDomainUtils $sharedDomainUtils; |
51 | |
52 | /** |
53 | * @param HookContainer $hookContainer |
54 | * @param Config $config |
55 | * @param CentralAuthTokenManager $tokenManager |
56 | * @param SharedDomainUtils $sharedDomainUtils |
57 | */ |
58 | public function __construct( |
59 | HookContainer $hookContainer, |
60 | Config $config, |
61 | CentralAuthTokenManager $tokenManager, |
62 | SharedDomainUtils $sharedDomainUtils |
63 | ) { |
64 | $this->caHookRunner = new CentralAuthHookRunner( $hookContainer ); |
65 | $this->config = $config; |
66 | $this->tokenManager = $tokenManager; |
67 | $this->sharedDomainUtils = $sharedDomainUtils; |
68 | } |
69 | |
70 | /** |
71 | * Start a central login redirect when it seems safe to do so. |
72 | * Otherwise, trigger edge login on the next request. |
73 | * |
74 | * @param User $user |
75 | * @param string &$inject_html |
76 | * @param bool|null $direct Was this directly after a login? (see T140853) |
77 | * @return bool |
78 | * |
79 | * @see SpecialCentralLogin |
80 | */ |
81 | public function onUserLoginComplete( $user, &$inject_html, $direct = null ) { |
82 | if ( !$this->config->get( CAMainConfigNames::CentralAuthCookies ) |
83 | || !$this->config->get( CAMainConfigNames::CentralAuthLoginWiki ) |
84 | || $this->sharedDomainUtils->isSul3Enabled( RequestContext::getMain()->getRequest() ) |
85 | ) { |
86 | // Use local sessions only. |
87 | return true; |
88 | } |
89 | |
90 | $logger = LoggerFactory::getInstance( 'CentralAuth' ); |
91 | $centralUser = CentralAuthUser::getInstance( $user ); |
92 | |
93 | $context = RequestContext::getMain(); |
94 | $request = $context->getRequest(); |
95 | |
96 | // Check that this is actually for a special login page view |
97 | $title = $context->getTitle(); |
98 | if ( $title && ( $title->isSpecial( 'Userlogin' ) || |
99 | $title->isSpecial( 'CreateAccount' ) ) |
100 | ) { |
101 | $logger->debug( 'CentralLogin triggered in UserLoginComplete' ); |
102 | $redirectUrl = $this->getRedirectUrl( |
103 | $request->getSession(), |
104 | $centralUser, |
105 | $request->getVal( 'returnto', '' ), |
106 | $request->getVal( 'returntoquery', '' ), |
107 | '', |
108 | $title->isSpecial( 'CreateAccount' ) ? 'signup' : '' |
109 | ); |
110 | $context->getOutput()->redirect( $redirectUrl ); |
111 | // Set $inject_html to some text to bypass the LoginForm redirection |
112 | $inject_html .= '<!-- do CentralAuth redirect -->'; |
113 | } |
114 | |
115 | return true; |
116 | } |
117 | |
118 | /** |
119 | * Initiate a central login redirect that sets up the central session for the temp user, |
120 | * then returns. |
121 | * |
122 | * @param Session $session |
123 | * @param UserIdentity $user |
124 | * @param string $returnTo |
125 | * @param string $returnToQuery |
126 | * @param string $returnToAnchor |
127 | * @param string &$redirectUrl |
128 | * @return bool |
129 | * |
130 | * @see SpecialCentralLogin |
131 | */ |
132 | public function onTempUserCreatedRedirect( |
133 | Session $session, |
134 | UserIdentity $user, |
135 | string $returnTo, |
136 | string $returnToQuery, |
137 | string $returnToAnchor, |
138 | &$redirectUrl |
139 | ) { |
140 | if ( !$this->config->get( CAMainConfigNames::CentralAuthLoginWiki ) ) { |
141 | return true; |
142 | } |
143 | |
144 | $logger = LoggerFactory::getInstance( 'CentralAuth' ); |
145 | $centralUser = CentralAuthUser::getPrimaryInstance( $user ); |
146 | |
147 | $logger->debug( 'CentralLogin triggered in TempUserCreatedRedirect' ); |
148 | $redirectUrl = $this->getRedirectUrl( |
149 | $session, |
150 | $centralUser, |
151 | $returnTo, |
152 | $returnToQuery, |
153 | $returnToAnchor, |
154 | 'signup' |
155 | ); |
156 | return false; |
157 | } |
158 | |
159 | /** |
160 | * Sets up central login so the caller can start it. |
161 | * - Stores a random-generated login secret, along with generic information about the |
162 | * user and the returnTo target, in the local session. |
163 | * - Composes an URL to the next step of the central login, Special:CentralLogin/start, and |
164 | * uses the token store and a query parameter in the URL to pass the secret, and information |
165 | * about the user and the session, in a secure way. |
166 | * - Returns the redirect URL. |
167 | * |
168 | * @param Session $session |
169 | * @param CentralAuthUser $centralUser |
170 | * @param string $returnTo |
171 | * @param string $returnToQuery |
172 | * @param string $returnToAnchor |
173 | * @param string $loginType 'signup' or the empty string for normal login |
174 | * @return string |
175 | * |
176 | * @see SpecialCentralLogin |
177 | */ |
178 | private function getRedirectUrl( |
179 | Session $session, |
180 | CentralAuthUser $centralUser, |
181 | $returnTo, |
182 | $returnToQuery, |
183 | $returnToAnchor, |
184 | $loginType |
185 | ) { |
186 | // User will be redirected to Special:CentralLogin/start (central wiki), |
187 | // then redirected back to Special:CentralLogin/complete (this wiki). |
188 | // Sanity check that "returnto" is not one of the central login pages. If it |
189 | // is, then clear the "returnto" options (LoginForm will use the main page). |
190 | $returnToTitle = Title::newFromText( $returnTo ); |
191 | if ( $returnToTitle && $returnToTitle->isSpecial( 'CentralLogin' ) ) { |
192 | $returnTo = ''; |
193 | $returnToQuery = ''; |
194 | } |
195 | |
196 | $remember = $session->shouldRememberUser(); |
197 | |
198 | // When POSTs triggered from Special:CentralLogin/start are sent back to |
199 | // this wiki, the token will be checked to see if it was signed with this. |
200 | // This is needed as Special:CentralLogin/start only takes a token argument |
201 | // and we need to make sure an agent requesting such a URL actually initiated |
202 | // the login request that spawned that token server-side. |
203 | $secret = MWCryptRand::generateHex( 32 ); |
204 | $session->set( 'CentralAuth:autologin:current-attempt', [ |
205 | 'secret' => $secret, |
206 | 'remember' => $remember, |
207 | 'returnTo' => $returnTo, |
208 | 'returnToQuery' => $returnToQuery, |
209 | 'returnToAnchor' => $returnToAnchor, |
210 | 'type' => $loginType |
211 | ] ); |
212 | |
213 | // Create a new token to pass to Special:CentralLogin/start (central wiki) |
214 | $data = [ |
215 | 'secret' => $secret, |
216 | 'name' => $centralUser->getName(), |
217 | 'guid' => $centralUser->getId(), |
218 | 'wikiId' => WikiMap::getCurrentWikiId(), |
219 | ]; |
220 | $this->caHookRunner->onCentralAuthLoginRedirectData( $centralUser, $data ); |
221 | $token = $this->tokenManager->tokenize( $data, 'central-login-start-token' ); |
222 | |
223 | $query = [ 'token' => $token ]; |
224 | $wiki = WikiMap::getWiki( $this->config->get( CAMainConfigNames::CentralAuthLoginWiki ) ); |
225 | return wfAppendQuery( $wiki->getCanonicalUrl( 'Special:CentralLogin/start' ), $query ); |
226 | } |
227 | } |