Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
14.96% |
41 / 274 |
|
15.79% |
3 / 19 |
CRAP | |
0.00% |
0 / 1 |
CentralAuthSessionProvider | |
14.96% |
41 / 274 |
|
15.79% |
3 / 19 |
4959.75 | |
0.00% |
0 / 1 |
__construct | |
76.92% |
10 / 13 |
|
0.00% |
0 / 1 |
2.05 | |||
postInitSetup | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
2 | |||
returnParentSessionInfo | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
12 | |||
provideSessionInfo | |
0.00% |
0 / 79 |
|
0.00% |
0 / 1 |
552 | |||
refreshSessionInfo | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
110 | |||
sessionIdWasReset | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
sessionDataToExport | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
cookieDataToExport | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
persistSession | |
0.00% |
0 / 45 |
|
0.00% |
0 / 1 |
240 | |||
unpersistSession | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 | |||
invalidateSessionsForUser | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
20 | |||
preventSessionsForUser | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
30 | |||
setForceHTTPSCookie | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setLoggedOutCookie | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
getVaryCookies | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
suggestLoginUsername | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
5.05 | |||
getCentralCookieDomain | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getExtendedLoginCookies | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getRememberUserDuration | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | use MediaWiki\Context\RequestContext; |
4 | use MediaWiki\Extension\CentralAuth\CentralAuthSessionManager; |
5 | use MediaWiki\Extension\CentralAuth\Config\CAMainConfigNames; |
6 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
7 | use MediaWiki\MainConfigNames; |
8 | use MediaWiki\Password\InvalidPassword; |
9 | use MediaWiki\Password\PasswordError; |
10 | use MediaWiki\Password\PasswordFactory; |
11 | use MediaWiki\Request\FauxRequest; |
12 | use MediaWiki\Request\WebRequest; |
13 | use MediaWiki\Session\CookieSessionProvider; |
14 | use MediaWiki\Session\SessionBackend; |
15 | use MediaWiki\Session\SessionInfo; |
16 | use MediaWiki\Session\SessionManager; |
17 | use MediaWiki\Session\UserInfo; |
18 | use MediaWiki\User\TempUser\TempUserConfig; |
19 | use MediaWiki\User\User; |
20 | use MediaWiki\User\UserIdentityLookup; |
21 | use MediaWiki\User\UserRigorOptions; |
22 | use Wikimedia\Rdbms\IDBAccessObject; |
23 | |
24 | /** |
25 | * CentralAuth cookie-based sessions. |
26 | * |
27 | * This is intended to completely replace the core CookieSessionProvider. |
28 | * |
29 | * @warning Due to the complicated way CentralAuth has historically interacted with core |
30 | * sessions, this is somewhat complicated and is probably not a good example to |
31 | * copy if you're writing your own SessionProvider. |
32 | */ |
33 | class CentralAuthSessionProvider extends CookieSessionProvider { |
34 | |
35 | /** @var bool */ |
36 | protected $enable = false; |
37 | |
38 | /** @var array */ |
39 | protected $centralCookieOptions = []; |
40 | |
41 | private UserIdentityLookup $userIdentityLookup; |
42 | private CentralAuthSessionManager $sessionManager; |
43 | private TempUserConfig $tempUserConfig; |
44 | |
45 | /** |
46 | * @param TempUserConfig $tempUserConfig |
47 | * @param UserIdentityLookup $userIdentityLookup |
48 | * @param CentralAuthSessionManager $sessionManager |
49 | * @param array $params In addition to the parameters for |
50 | * CookieSessionProvider, the following are |
51 | * recognized: |
52 | * - enable: Whether to set CentralAuth-specific features. Defaults to |
53 | * $wgCentralAuthCookies. |
54 | * - centralSessionName: Central session cookie name. Defaults to |
55 | * centralCookieOptions['prefix'] . 'Session'. Note this does not |
56 | * replace the parent class's 'sessionName', it's a different cookie. |
57 | * - centralCookieOptions: Settings for central cookies |
58 | * - prefix: Cookie prefix, defaults to $wgCentralAuthCookiePrefix |
59 | * - path: Cookie path, defaults to $wgCentralAuthCookiePath |
60 | * - domain: Cookie domain, defaults to $wgCentralAuthCookieDomain |
61 | * - secure: Cookie secure flag, defaults to $wgCookieSecure |
62 | * - httpOnly: Cookie httpOnly flag, defaults to $wgCookieHttpOnly |
63 | * - sameSite: Cookie SameSite attribute, defaults to $wgCookieSameSite |
64 | */ |
65 | public function __construct( |
66 | TempUserConfig $tempUserConfig, |
67 | UserIdentityLookup $userIdentityLookup, |
68 | CentralAuthSessionManager $sessionManager, |
69 | $params = [] |
70 | ) { |
71 | $this->userIdentityLookup = $userIdentityLookup; |
72 | $this->sessionManager = $sessionManager; |
73 | $this->tempUserConfig = $tempUserConfig; |
74 | |
75 | $params += [ |
76 | 'centralCookieOptions' => [], |
77 | ]; |
78 | |
79 | if ( !is_array( $params['centralCookieOptions'] ) ) { |
80 | throw new \InvalidArgumentException( |
81 | __METHOD__ . ': centralCookieOptions must be an array' |
82 | ); |
83 | } |
84 | |
85 | $this->centralCookieOptions = $params['centralCookieOptions']; |
86 | unset( $params['centralCookieOptions'] ); |
87 | |
88 | parent::__construct( $params ); |
89 | } |
90 | |
91 | protected function postInitSetup() { |
92 | parent::postInitSetup(); |
93 | |
94 | $this->centralCookieOptions += [ |
95 | 'prefix' => $this->getConfig()->get( CAMainConfigNames::CentralAuthCookiePrefix ), |
96 | 'path' => $this->getConfig()->get( CAMainConfigNames::CentralAuthCookiePath ), |
97 | 'domain' => $this->getConfig()->get( CAMainConfigNames::CentralAuthCookieDomain ), |
98 | 'secure' => $this->getConfig()->get( MainConfigNames::CookieSecure ) || |
99 | $this->getConfig()->get( MainConfigNames::ForceHTTPS ), |
100 | 'httpOnly' => $this->getConfig()->get( MainConfigNames::CookieHttpOnly ), |
101 | 'sameSite' => $this->getConfig()->get( MainConfigNames::CookieSameSite ) |
102 | ]; |
103 | |
104 | $params = [ |
105 | 'enable' => $this->getConfig()->get( CAMainConfigNames::CentralAuthCookies ), |
106 | 'centralSessionName' => $this->centralCookieOptions['prefix'] . 'Session', |
107 | ]; |
108 | $this->params += $params; |
109 | |
110 | $this->enable = (bool)$params['enable']; |
111 | } |
112 | |
113 | /** |
114 | * Get the local session info, with CentralAuthSource metadata. |
115 | * |
116 | * @param WebRequest $request |
117 | * @param bool $forceEmptyPersist If this is true and there is no local |
118 | * session, return a persistent empty local session to override the |
119 | * non-functional CentralAuth one. This prevents the deletion of global |
120 | * cookies, allowing the user to retain their central login elsewhere |
121 | * in the same cookie domain. It makes sense to do this for problems with |
122 | * the central session that are specific to the local wiki. (T342475) |
123 | * @return SessionInfo|null |
124 | */ |
125 | private function returnParentSessionInfo( WebRequest $request, $forceEmptyPersist = false ) { |
126 | $info = parent::provideSessionInfo( $request ); |
127 | if ( $info ) { |
128 | return new SessionInfo( $info->getPriority(), [ |
129 | 'copyFrom' => $info, |
130 | 'metadata' => [ |
131 | 'CentralAuthSource' => 'Local', |
132 | ], |
133 | ] ); |
134 | } |
135 | |
136 | if ( $forceEmptyPersist ) { |
137 | return new SessionInfo( $this->priority, [ |
138 | 'id' => null, |
139 | 'provider' => $this, |
140 | 'idIsSafe' => true, |
141 | 'persisted' => true, |
142 | 'metadata' => [ |
143 | 'CentralAuthSource' => 'Local', |
144 | ], |
145 | ] ); |
146 | } |
147 | |
148 | return null; |
149 | } |
150 | |
151 | /** |
152 | * Determine whether $request has a valid CentralAuth session. |
153 | * |
154 | * The following requests are considered to have a valid CentralAuth session: |
155 | * - A request with a centralauth_User and centralauth_Token cookie (prefix is configurable) |
156 | * that match the data in the globaluser table. |
157 | * - A request with a centralauth_Session cookie where the referenced data exists in the |
158 | * central session store, and the username and token in the central session object match |
159 | * the data in the globaluser table. |
160 | * |
161 | * The following requests are considered to have a valid local session (marked with |
162 | * CentralAuthSource => Local in the session metadata): |
163 | * - A request with a centralauth_Session cookie where the referenced data exists in the |
164 | * central session store, but is a stub (has pending_name or pending_guid fields). |
165 | * - Anything that CookieSessionProvider considers valid, if the session is anonymous or for |
166 | * a local user that's not attached to the global CentralAuth user. |
167 | * - When $wgCentralAuthCookies is disabled, falls back to CookieSessionProvider entirely. |
168 | * |
169 | * @param WebRequest $request |
170 | * @return SessionInfo|null |
171 | */ |
172 | public function provideSessionInfo( WebRequest $request ) { |
173 | if ( !$this->enable ) { |
174 | $this->logger->debug( __METHOD__ . ': Not enabled, falling back to core sessions' ); |
175 | return $this->returnParentSessionInfo( $request ); |
176 | } |
177 | |
178 | $info = [ |
179 | 'id' => $this->getCookie( $request, $this->params['sessionName'], '' ) |
180 | ]; |
181 | if ( !SessionManager::validateSessionId( $info['id'] ) ) { |
182 | unset( $info['id'] ); |
183 | } |
184 | |
185 | $userName = null; |
186 | $token = null; |
187 | $from = null; |
188 | |
189 | $prefix = $this->centralCookieOptions['prefix']; |
190 | $userCookie = $this->getCookie( $request, 'User', $prefix ); |
191 | $tokenCookie = $this->getCookie( $request, 'Token', $prefix ); |
192 | if ( $userCookie !== null && $tokenCookie !== null ) { |
193 | $userName = $userCookie; |
194 | $token = $tokenCookie; |
195 | $from = 'cookies'; |
196 | } else { |
197 | $id = $this->getCookie( $request, $this->params['centralSessionName'], '' ); |
198 | if ( $id !== null ) { |
199 | $data = $this->sessionManager->getCentralSessionById( $id ); |
200 | if ( isset( $data['pending_name'] ) || isset( $data['pending_guid'] ) ) { |
201 | $this->logger->debug( __METHOD__ . ': uninitialized session' ); |
202 | } elseif ( isset( $data['token'] ) && isset( $data['user'] ) ) { |
203 | $token = $data['token']; |
204 | $userName = $data['user']; |
205 | $from = 'session'; |
206 | } else { |
207 | $this->logger->debug( __METHOD__ . ': uninitialized session' ); |
208 | } |
209 | } |
210 | } |
211 | if ( $userName === null || $token === null ) { |
212 | return $this->returnParentSessionInfo( $request ); |
213 | } |
214 | |
215 | // Check to avoid session ID collisions, as reported on T21158 |
216 | if ( $userCookie === null ) { |
217 | $this->logger->debug( |
218 | __METHOD__ . ': no User cookie, so unable to check for session mismatch' |
219 | ); |
220 | return $this->returnParentSessionInfo( $request ); |
221 | } |
222 | |
223 | if ( $userCookie != $userName ) { |
224 | $this->logger->debug( |
225 | __METHOD__ . ': Session ID/User mismatch. Possible session collision. ' . |
226 | "Expected: $userName; actual: $userCookie" |
227 | ); |
228 | return $this->returnParentSessionInfo( $request ); |
229 | } |
230 | |
231 | // Clean up username |
232 | $userName = $this->userNameUtils->getCanonical( $userName ); |
233 | if ( !$userName ) { |
234 | $this->logger->debug( __METHOD__ . ': invalid username' ); |
235 | return $this->returnParentSessionInfo( $request ); |
236 | } |
237 | if ( !$this->userNameUtils->isUsable( $userName ) ) { |
238 | // Log a warning if the username is not usable, except if the name is reserved by the temporary account |
239 | // system to avoid spamming the logs (T373827). |
240 | if ( !$this->tempUserConfig->isReservedName( $userName ) ) { |
241 | $this->logger->warning( |
242 | __METHOD__ . ': username {username} is not usable on this wiki', [ |
243 | 'username' => $userName, |
244 | ] |
245 | ); |
246 | } |
247 | return $this->returnParentSessionInfo( $request, true ); |
248 | } |
249 | |
250 | // Try the central user |
251 | $centralUser = CentralAuthUser::getInstanceByName( $userName ); |
252 | |
253 | // Skip if they're being renamed |
254 | if ( $centralUser->renameInProgress() ) { |
255 | $this->logger->debug( __METHOD__ . ': rename in progress' ); |
256 | // No fallback here, just fail it because our SessionCheckMetadata |
257 | // hook will do so anyway. |
258 | return null; |
259 | } |
260 | |
261 | if ( !$centralUser->exists() ) { |
262 | $this->logger->debug( __METHOD__ . ': global account doesn\'t exist' ); |
263 | return $this->returnParentSessionInfo( $request ); |
264 | } |
265 | if ( !$centralUser->isAttached() ) { |
266 | $userIdentity = $this->userIdentityLookup->getUserIdentityByName( $userName ); |
267 | if ( $userIdentity && $userIdentity->isRegistered() ) { |
268 | $this->logger->debug( __METHOD__ . ': not attached and local account exists' ); |
269 | return $this->returnParentSessionInfo( $request, true ); |
270 | } |
271 | } |
272 | if ( $centralUser->authenticateWithToken( $token ) != 'ok' ) { |
273 | $this->logger->debug( __METHOD__ . ': token mismatch' ); |
274 | // At this point, don't log in with a local session anymore |
275 | return null; |
276 | } |
277 | |
278 | $this->logger->debug( __METHOD__ . ": logged in from $from" ); |
279 | |
280 | $info += [ |
281 | 'userInfo' => UserInfo::newFromName( $userName, true ), |
282 | 'provider' => $this, |
283 | // CA sessions are always persistent |
284 | 'persisted' => true, |
285 | 'remembered' => $tokenCookie !== null, |
286 | 'metadata' => [ |
287 | 'CentralAuthSource' => 'CentralAuth', |
288 | ], |
289 | ]; |
290 | |
291 | return new SessionInfo( $this->priority, $info ); |
292 | } |
293 | |
294 | /** @inheritDoc */ |
295 | public function refreshSessionInfo( SessionInfo $info, WebRequest $request, &$metadata ) { |
296 | // Check on the metadata, to avoid T124409 |
297 | if ( isset( $metadata['CentralAuthSource'] ) ) { |
298 | $name = $info->getUserInfo()->getName(); |
299 | if ( $name === null ) { |
300 | return true; |
301 | } |
302 | |
303 | $source = 'Local'; |
304 | if ( $this->enable ) { |
305 | $centralUser = CentralAuthUser::getInstanceByName( $name ); |
306 | $centralUserExists = $centralUser->exists(); |
307 | if ( $centralUserExists && $centralUser->isAttached() ) { |
308 | $source = 'CentralAuth'; |
309 | } elseif ( $centralUserExists ) { |
310 | $userIdentity = $this->userIdentityLookup->getUserIdentityByName( |
311 | $name, |
312 | IDBAccessObject::READ_LATEST |
313 | ); |
314 | if ( !$userIdentity || !$userIdentity->isRegistered() ) { |
315 | $source = 'CentralAuth'; |
316 | } |
317 | } |
318 | } |
319 | if ( $metadata['CentralAuthSource'] !== $source ) { |
320 | $this->logger->warning( |
321 | 'Session "{session}": CentralAuth saved source {saved} != expected source {expected}', [ |
322 | 'session' => $info->__toString(), |
323 | 'saved' => $metadata['CentralAuthSource'], |
324 | 'expected' => $source, |
325 | ] |
326 | ); |
327 | |
328 | return false; |
329 | } |
330 | } |
331 | |
332 | return true; |
333 | } |
334 | |
335 | /** @inheritDoc */ |
336 | public function sessionIdWasReset( SessionBackend $session, $oldId ) { |
337 | if ( !$this->enable ) { |
338 | return; |
339 | } |
340 | |
341 | // We need a Session to pass to CentralAuthSessionManager::setCentralSession() |
342 | // to reset the session ID, so create one on a new FauxRequest. |
343 | $s = $session->getSession( new FauxRequest() ); |
344 | |
345 | // We also need to fetch the current central data to pass to |
346 | // CentralAuthSessionManager::setCentralSession() when resetting the ID. |
347 | $data = $this->sessionManager->getCentralSession( $s ); |
348 | |
349 | $this->sessionManager->setCentralSession( $data, true, $s ); |
350 | } |
351 | |
352 | /** @inheritDoc */ |
353 | protected function sessionDataToExport( $user ) { |
354 | $data = parent::sessionDataToExport( $user ); |
355 | |
356 | // CentralAuth needs to prevent core login-from-session to |
357 | // avoid bugs like T124409 |
358 | $centralUser = CentralAuthUser::getInstance( $user ); |
359 | if ( $centralUser->isAttached() ) { |
360 | unset( $data['wsToken'] ); |
361 | } |
362 | |
363 | return $data; |
364 | } |
365 | |
366 | /** @inheritDoc */ |
367 | protected function cookieDataToExport( $user, $remember ) { |
368 | // If we're going to set CA cookies, don't remember in core cookies. |
369 | if ( $this->enable && $remember ) { |
370 | $centralUser = CentralAuthUser::getInstance( $user ); |
371 | $remember = !$centralUser->isAttached(); |
372 | } |
373 | |
374 | return parent::cookieDataToExport( $user, $remember ); |
375 | } |
376 | |
377 | /** |
378 | * Sets CentralAuth and core authentication cookies. |
379 | * |
380 | * - For authenticated sessions where the local account is owned by the corresponding central |
381 | * account, the core <wiki>UserToken cookie is not set, the centralauth_Session, |
382 | * centralauth_User, and (if the session is remembered) centralauth_Token cookies are set, |
383 | * and a new central session object is created in the central session store if there wasn't |
384 | * already a centralauth_Session cookie pointing at a valid object. |
385 | * - Otherwise (including stub central sessions), behavior is identical to CookieSessionProvider. |
386 | * |
387 | * @param SessionBackend $session |
388 | * @param WebRequest $request |
389 | */ |
390 | public function persistSession( SessionBackend $session, WebRequest $request ) { |
391 | parent::persistSession( $session, $request ); |
392 | |
393 | if ( !$this->enable ) { |
394 | return; |
395 | } |
396 | |
397 | $response = $request->response(); |
398 | if ( $response->headersSent() ) { |
399 | // Can't do anything now |
400 | return; |
401 | } |
402 | |
403 | $s = $session->getSession( $request ); |
404 | |
405 | $user = $session->getUser(); |
406 | $centralUser = CentralAuthUser::getInstance( $user ); |
407 | |
408 | if ( $centralUser->exists() && ( $centralUser->isAttached() || !$user->getId() ) ) { |
409 | // CentralAuth needs to prevent core login-from-session to |
410 | // avoid bugs like T124409 |
411 | $data = &$session->getData(); |
412 | if ( array_key_exists( 'wsToken', $data ) ) { |
413 | unset( $data['wsToken'] ); |
414 | $session->dirty(); |
415 | } |
416 | unset( $data ); |
417 | |
418 | $metadata = $session->getProviderMetadata(); |
419 | $metadata['CentralAuthSource'] = 'CentralAuth'; |
420 | $session->setProviderMetadata( $metadata ); |
421 | |
422 | $remember = $session->shouldRememberUser(); |
423 | |
424 | $options = $this->centralCookieOptions; |
425 | |
426 | // We only save the user into the central session if it's not a |
427 | // "pending" session, but we still need the ID to set the cookie. |
428 | $centralSessionId = $s->get( 'CentralAuth::centralSessionId' ) |
429 | ?: $this->getCookie( $request, $this->params['centralSessionName'], '' ) |
430 | ?: false; |
431 | $data = $centralSessionId ? $this->sessionManager->getCentralSessionById( $centralSessionId ) : []; |
432 | if ( isset( $data['pending_name'] ) ) { |
433 | $remember = false; |
434 | } else { |
435 | $data['user'] = $centralUser->getName(); |
436 | $data['token'] = $centralUser->getAuthToken(); |
437 | $data['remember'] = $remember; |
438 | } |
439 | $centralSessionId = $this->sessionManager->setCentralSession( $data, $centralSessionId, $s ); |
440 | |
441 | $cookies = [ |
442 | 'User' => (string)$centralUser->getName(), |
443 | 'Token' => $remember ? (string)$centralUser->getAuthToken() : false, |
444 | ]; |
445 | foreach ( $cookies as $name => $value ) { |
446 | if ( $value === false ) { |
447 | $response->clearCookie( $name, $options ); |
448 | } else { |
449 | $expirationDuration = $this->getLoginCookieExpiration( $name, $remember ); |
450 | $expiration = $expirationDuration ? $expirationDuration + time() : null; |
451 | $response->setCookie( $name, (string)$value, $expiration, $options ); |
452 | } |
453 | } |
454 | |
455 | $response->setCookie( $this->params['centralSessionName'], $centralSessionId, null, |
456 | [ 'prefix' => '' ] + $options ); |
457 | } else { |
458 | $metadata = $session->getProviderMetadata(); |
459 | $metadata['CentralAuthSource'] = 'Local'; |
460 | $session->setProviderMetadata( $metadata ); |
461 | } |
462 | } |
463 | |
464 | /** |
465 | * Deletes CentralAuth and core authentication cookies. |
466 | * |
467 | * @param WebRequest $request |
468 | */ |
469 | public function unpersistSession( WebRequest $request ) { |
470 | parent::unpersistSession( $request ); |
471 | |
472 | if ( !$this->enable ) { |
473 | return; |
474 | } |
475 | |
476 | $response = $request->response(); |
477 | if ( $response->headersSent() ) { |
478 | // Can't do anything now |
479 | $this->logger->debug( __METHOD__ . ': Headers already sent' ); |
480 | return; |
481 | } |
482 | |
483 | $expiry = time() - 86400; |
484 | $response->clearCookie( 'User', $this->centralCookieOptions ); |
485 | $response->clearCookie( 'Token', $this->centralCookieOptions ); |
486 | $response->clearCookie( $this->params['centralSessionName'], |
487 | [ 'prefix' => '' ] + $this->centralCookieOptions ); |
488 | } |
489 | |
490 | /** @inheritDoc */ |
491 | public function invalidateSessionsForUser( User $user ) { |
492 | $centralUser = CentralAuthUser::getPrimaryInstance( $user ); |
493 | if ( $centralUser->exists() && ( $centralUser->isAttached() || !$user->isRegistered() ) ) { |
494 | $centralUser->resetAuthToken(); |
495 | } |
496 | } |
497 | |
498 | /** @inheritDoc */ |
499 | public function preventSessionsForUser( $username ) { |
500 | $username = $this->userNameUtils->getCanonical( $username ); |
501 | if ( !$username ) { |
502 | return; |
503 | } |
504 | |
505 | $centralUser = CentralAuthUser::getPrimaryInstanceByName( $username ); |
506 | if ( !$centralUser->exists() ) { |
507 | return; |
508 | } |
509 | |
510 | // Reset the user's password to something invalid and reset the token |
511 | // if it's not already invalid. |
512 | $config = RequestContext::getMain()->getConfig(); |
513 | $passwordFactory = new PasswordFactory( |
514 | $config->get( MainConfigNames::PasswordConfig ), |
515 | $config->get( MainConfigNames::PasswordDefault ) |
516 | ); |
517 | |
518 | try { |
519 | $password = $passwordFactory->newFromCiphertext( $centralUser->getPassword() ); |
520 | } catch ( PasswordError $e ) { |
521 | return; |
522 | } |
523 | if ( !$password instanceof InvalidPassword ) { |
524 | $centralUser->setPassword( null ); |
525 | } |
526 | } |
527 | |
528 | /** |
529 | * @inheritDoc |
530 | */ |
531 | protected function setForceHTTPSCookie( $set, ?SessionBackend $backend, WebRequest $request ) { |
532 | // Do nothing. We don't support mixed-protocol HTTP/HTTPS wikis in CentralAuth, |
533 | // so this cookie is not needed. |
534 | } |
535 | |
536 | /** @inheritDoc */ |
537 | protected function setLoggedOutCookie( $loggedOut, WebRequest $request ) { |
538 | if ( $loggedOut + 86400 > time() && |
539 | $loggedOut !== (int)$this->getCookie( |
540 | $request, 'LoggedOut', $this->centralCookieOptions['prefix'] ) |
541 | ) { |
542 | $request->response()->setCookie( 'LoggedOut', (string)$loggedOut, $loggedOut + 86400, |
543 | $this->centralCookieOptions ); |
544 | } |
545 | } |
546 | |
547 | /** |
548 | * @return string[] |
549 | */ |
550 | public function getVaryCookies() { |
551 | $cookies = parent::getVaryCookies(); |
552 | |
553 | if ( $this->enable ) { |
554 | $prefix = $this->centralCookieOptions['prefix']; |
555 | $cookies[] = $prefix . 'Token'; |
556 | $cookies[] = $prefix . 'LoggedOut'; |
557 | $cookies[] = $this->params['centralSessionName']; |
558 | } |
559 | |
560 | return $cookies; |
561 | } |
562 | |
563 | /** @inheritDoc */ |
564 | public function suggestLoginUsername( WebRequest $request ) { |
565 | $name = $this->getCookie( $request, 'User', $this->centralCookieOptions['prefix'] ); |
566 | if ( $name !== null ) { |
567 | if ( $this->userNameUtils->isTemp( $name ) ) { |
568 | $name = false; |
569 | } else { |
570 | $name = $this->userNameUtils->getCanonical( $name, UserRigorOptions::RIGOR_USABLE ); |
571 | } |
572 | } |
573 | return ( $name === false || $name === null ) |
574 | ? parent::suggestLoginUsername( $request ) |
575 | : $name; |
576 | } |
577 | |
578 | /** |
579 | * Fetch the central cookie domain |
580 | * @return string |
581 | */ |
582 | public function getCentralCookieDomain() { |
583 | return $this->centralCookieOptions['domain']; |
584 | } |
585 | |
586 | /** @inheritDoc */ |
587 | protected function getExtendedLoginCookies() { |
588 | $cookies = parent::getExtendedLoginCookies(); |
589 | $cookies[] = 'User'; |
590 | return $cookies; |
591 | } |
592 | |
593 | /** @inheritDoc */ |
594 | public function getRememberUserDuration() { |
595 | // CentralAuth needs User and Token cookies to remember the user. The fallback to |
596 | // sessions needs UserID as well, so if that one has shorter expiration, the remember |
597 | // duration will depend on whether the account is attached; let's return the shorter |
598 | // duration in that case. |
599 | |
600 | return min( |
601 | $this->getLoginCookieExpiration( 'User', true ), |
602 | $this->getLoginCookieExpiration( 'Token', true ), |
603 | $this->getLoginCookieExpiration( 'UserID', true ) |
604 | ) ?: null; |
605 | } |
606 | } |